Merge pull request #4696 from jtattermusch/win_static_deps

Link grpc_csharp_ext dependencies on Windows statically
diff --git a/.gitignore b/.gitignore
index f059eca..471649d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,8 +4,13 @@
 libs
 objs
 
-# Python virtual environments
-python*_virtual_environment
+# Python items
+.coverage*
+.eggs
+.tox
+htmlcov/
+dist/
+*.egg
 
 # gcov coverage data
 reports
diff --git a/.rspec b/.rspec
new file mode 100755
index 0000000..b94d663
--- /dev/null
+++ b/.rspec
@@ -0,0 +1,6 @@
+-Isrc/ruby
+-Isrc/ruby/pb
+--backtrace
+--require spec/spec_helper
+--format documentation
+--color
diff --git a/BUILD b/BUILD
index 56b5c12..c02fb9d 100644
--- a/BUILD
+++ b/BUILD
@@ -385,6 +385,7 @@
     "src/core/surface/server.c",
     "src/core/surface/server_chttp2.c",
     "src/core/surface/server_create.c",
+    "src/core/surface/validate_metadata.c",
     "src/core/surface/version.c",
     "src/core/transport/byte_stream.c",
     "src/core/transport/chttp2/alpn.c",
@@ -656,6 +657,7 @@
     "src/core/surface/server.c",
     "src/core/surface/server_chttp2.c",
     "src/core/surface/server_create.c",
+    "src/core/surface/validate_metadata.c",
     "src/core/surface/version.c",
     "src/core/transport/byte_stream.c",
     "src/core/transport/chttp2/alpn.c",
@@ -1190,6 +1192,7 @@
     "src/core/surface/server.c",
     "src/core/surface/server_chttp2.c",
     "src/core/surface/server_create.c",
+    "src/core/surface/validate_metadata.c",
     "src/core/surface/version.c",
     "src/core/transport/byte_stream.c",
     "src/core/transport/chttp2/alpn.c",
diff --git a/src/ruby/Gemfile b/Gemfile
similarity index 100%
rename from src/ruby/Gemfile
rename to Gemfile
diff --git a/MANIFEST.md b/MANIFEST.md
new file mode 100644
index 0000000..b523f8f
--- /dev/null
+++ b/MANIFEST.md
@@ -0,0 +1,14 @@
+# Top-level Items by language
+
+## Node
+* [binding.gyp](binding.gyp)
+
+## Objective-C
+* [gRPC.podspec](gRPC.podspec)
+
+## Python
+* [requirements.txt](requirements.txt)
+* [setup.cfg](setup.cfg)
+* [setup.py](setup.py)
+* [tox.ini](tox.ini)
+* [PYTHON-MANIFEST.in](PYTHON-MANIFEST.in)
diff --git a/Makefile b/Makefile
index a62f95a..87279c6 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@
 # This file can be regenerated from the template by running
 # tools/buildgen/generate_projects.sh
 
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -903,6 +903,7 @@
 cxx_string_ref_test: $(BINDIR)/$(CONFIG)/cxx_string_ref_test
 cxx_time_test: $(BINDIR)/$(CONFIG)/cxx_time_test
 end2end_test: $(BINDIR)/$(CONFIG)/end2end_test
+generic_async_streaming_ping_pong_test: $(BINDIR)/$(CONFIG)/generic_async_streaming_ping_pong_test
 generic_end2end_test: $(BINDIR)/$(CONFIG)/generic_end2end_test
 grpc_cli: $(BINDIR)/$(CONFIG)/grpc_cli
 grpc_cpp_plugin: $(BINDIR)/$(CONFIG)/grpc_cpp_plugin
@@ -1256,6 +1257,7 @@
   $(BINDIR)/$(CONFIG)/cxx_string_ref_test \
   $(BINDIR)/$(CONFIG)/cxx_time_test \
   $(BINDIR)/$(CONFIG)/end2end_test \
+  $(BINDIR)/$(CONFIG)/generic_async_streaming_ping_pong_test \
   $(BINDIR)/$(CONFIG)/generic_end2end_test \
   $(BINDIR)/$(CONFIG)/grpc_cli \
   $(BINDIR)/$(CONFIG)/interop_client \
@@ -1555,6 +1557,8 @@
 	$(Q) $(BINDIR)/$(CONFIG)/cxx_time_test || ( echo test cxx_time_test failed ; exit 1 )
 	$(E) "[RUN]     Testing end2end_test"
 	$(Q) $(BINDIR)/$(CONFIG)/end2end_test || ( echo test end2end_test failed ; exit 1 )
+	$(E) "[RUN]     Testing generic_async_streaming_ping_pong_test"
+	$(Q) $(BINDIR)/$(CONFIG)/generic_async_streaming_ping_pong_test || ( echo test generic_async_streaming_ping_pong_test failed ; exit 1 )
 	$(E) "[RUN]     Testing generic_end2end_test"
 	$(Q) $(BINDIR)/$(CONFIG)/generic_end2end_test || ( echo test generic_end2end_test failed ; exit 1 )
 	$(E) "[RUN]     Testing interop_test"
@@ -2418,6 +2422,7 @@
     src/core/surface/server.c \
     src/core/surface/server_chttp2.c \
     src/core/surface/server_create.c \
+    src/core/surface/validate_metadata.c \
     src/core/surface/version.c \
     src/core/transport/byte_stream.c \
     src/core/transport/chttp2/alpn.c \
@@ -2720,6 +2725,7 @@
     src/core/surface/server.c \
     src/core/surface/server_chttp2.c \
     src/core/surface/server_create.c \
+    src/core/surface/validate_metadata.c \
     src/core/surface/version.c \
     src/core/transport/byte_stream.c \
     src/core/transport/chttp2/alpn.c \
@@ -9325,6 +9331,49 @@
 endif
 
 
+GENERIC_ASYNC_STREAMING_PING_PONG_TEST_SRC = \
+    test/cpp/qps/generic_async_streaming_ping_pong_test.cc \
+
+GENERIC_ASYNC_STREAMING_PING_PONG_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GENERIC_ASYNC_STREAMING_PING_PONG_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/generic_async_streaming_ping_pong_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/generic_async_streaming_ping_pong_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/generic_async_streaming_ping_pong_test: $(PROTOBUF_DEP) $(GENERIC_ASYNC_STREAMING_PING_PONG_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(GENERIC_ASYNC_STREAMING_PING_PONG_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/generic_async_streaming_ping_pong_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/generic_async_streaming_ping_pong_test.o:  $(LIBDIR)/$(CONFIG)/libqps.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_generic_async_streaming_ping_pong_test: $(GENERIC_ASYNC_STREAMING_PING_PONG_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(GENERIC_ASYNC_STREAMING_PING_PONG_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 GENERIC_END2END_TEST_SRC = \
     test/cpp/end2end/generic_end2end_test.cc \
 
diff --git a/PYTHON-MANIFEST.in b/PYTHON-MANIFEST.in
new file mode 100644
index 0000000..02bd9b5
--- /dev/null
+++ b/PYTHON-MANIFEST.in
@@ -0,0 +1,10 @@
+graft src/python/grpcio/grpc
+graft src/python/grpcio/tests
+graft src/core
+graft include/grpc
+graft third_party/boringssl
+include src/python/grpcio/commands.py
+include src/python/grpcio/grpc_core_dependencies.py
+include src/python/grpcio/README.rst
+include requirements.txt
+include etc/roots.pem
diff --git a/README.md b/README.md
index 9701930..e0553ec 100644
--- a/README.md
+++ b/README.md
@@ -11,16 +11,16 @@
 
 #Installation
 
-See grpc/INSTALL for installation instructions for various platforms.
+See [grpc/INSTALL](INSTALL) for installation instructions for various platforms.
 
 #Repository Structure & Status
 
 This repository contains source code for gRPC libraries for multiple languages written on top of shared C core library [src/core] (src/core).
 
-Libraries in different languages are in different state of development. We are seeking contributions for all of these libraries.
+Libraries in different languages are in different states of development. We are seeking contributions for all of these libraries.
 
-| Language                | Source                              | Status                          |
-|-------------------------|-------------------------------------|---------------------------------|
+| Language                | Source                              | Status                           |
+|-------------------------|-------------------------------------|----------------------------------|
 | Shared C [core library] | [src/core] (src/core)               | Beta - the surface API is stable |
 | C++                     | [src/cpp] (src/cpp)                 | Beta - the surface API is stable |
 | Ruby                    | [src/ruby] (src/ruby)               | Beta - the surface API is stable |
@@ -31,10 +31,12 @@
 | Objective-C             | [src/objective-c] (src/objective-c) | Beta - the surface API is stable |
 
 <small>
-Java source code is in [grpc-java] (http://github.com/grpc/grpc-java) repository.
-Go source code is in [grpc-go] (http://github.com/grpc/grpc-go) repository.
+Java source code is in the [grpc-java] (http://github.com/grpc/grpc-java) repository.
+Go source code is in the [grpc-go] (http://github.com/grpc/grpc-go) repository.
 </small>
 
+See [MANIFEST.md](MANIFEST.md) for a listing of top-level items in the
+repository.
 
 #Overview
 
diff --git a/src/ruby/Rakefile b/Rakefile
similarity index 66%
rename from src/ruby/Rakefile
rename to Rakefile
index cc7832b..079df67 100755
--- a/src/ruby/Rakefile
+++ b/Rakefile
@@ -5,23 +5,28 @@
 require 'bundler/gem_tasks'
 
 # Add rubocop style checking tasks
-RuboCop::RakeTask.new
+RuboCop::RakeTask.new(:rubocop) do |task|
+  task.options = ['-c', 'src/ruby/.rubocop.yml']
+  task.patterns = ['src/ruby/{lib,spec}/**/*.rb']
+end
 
 # Add the extension compiler task
 Rake::ExtensionTask.new 'grpc' do |ext|
-  ext.lib_dir = File.join('lib', 'grpc')
+  ext.source_pattern = '**/*.{c,h}'
+  ext.ext_dir = File.join('src', 'ruby', 'ext', 'grpc')
+  ext.lib_dir = File.join('src', 'ruby', 'lib', 'grpc')
 end
 
 # Define the test suites
 SPEC_SUITES = [
-  { id: :wrapper, title: 'wrapper layer', files: %w(spec/*.rb) },
-  { id: :idiomatic, title: 'idiomatic layer', dir: %w(spec/generic),
+  { id: :wrapper, title: 'wrapper layer', files: %w(src/ruby/spec/*.rb) },
+  { id: :idiomatic, title: 'idiomatic layer', dir: %w(src/ruby/spec/generic),
     tags: ['~bidi', '~server'] },
-  { id: :bidi, title: 'bidi tests', dir: %w(spec/generic),
+  { id: :bidi, title: 'bidi tests', dir: %w(src/ruby/spec/generic),
     tag: 'bidi' },
-  { id: :server, title: 'rpc server thread tests', dir: %w(spec/generic),
+  { id: :server, title: 'rpc server thread tests', dir: %w(src/ruby/spec/generic),
     tag: 'server' },
-  { id: :pb, title: 'protobuf service tests', dir: %w(spec/pb) }
+  { id: :pb, title: 'protobuf service tests', dir: %w(src/ruby/spec/pb) }
 ]
 namespace :suite do
   SPEC_SUITES.each do |suite|
@@ -34,7 +39,7 @@
       if suite[:dir]
         suite[:dir].each { |f| spec_files += Dir["#{f}/**/*_spec.rb"] }
       end
-      helper = 'spec/spec_helper.rb'
+      helper = 'src/ruby/spec/spec_helper.rb'
       spec_files << helper unless spec_files.include?(helper)
 
       t.pattern = spec_files
diff --git a/binding.gyp b/binding.gyp
index e5b4b97..a5e9933 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -279,6 +279,7 @@
         'src/core/surface/server.c',
         'src/core/surface/server_chttp2.c',
         'src/core/surface/server_create.c',
+        'src/core/surface/validate_metadata.c',
         'src/core/surface/version.c',
         'src/core/transport/byte_stream.c',
         'src/core/transport/chttp2/alpn.c',
diff --git a/build.yaml b/build.yaml
index 736a917..70a8dee 100644
--- a/build.yaml
+++ b/build.yaml
@@ -316,6 +316,7 @@
   - src/core/surface/server.c
   - src/core/surface/server_chttp2.c
   - src/core/surface/server_create.c
+  - src/core/surface/validate_metadata.c
   - src/core/surface/version.c
   - src/core/transport/byte_stream.c
   - src/core/transport/chttp2/alpn.c
@@ -1951,6 +1952,23 @@
   - grpc
   - gpr_test_util
   - gpr
+- name: generic_async_streaming_ping_pong_test
+  build: test
+  language: c++
+  src:
+  - test/cpp/qps/generic_async_streaming_ping_pong_test.cc
+  deps:
+  - qps
+  - grpc++_test_util
+  - grpc_test_util
+  - grpc++
+  - grpc
+  - gpr_test_util
+  - gpr
+  platforms:
+  - mac
+  - linux
+  - posix
 - name: generic_end2end_test
   build: test
   language: c++
@@ -2477,3 +2495,8 @@
   - src/node/ext/server.cc
   - src/node/ext/server_credentials.cc
   - src/node/ext/timeval.cc
+python_dependencies:
+  deps:
+  - grpc
+  - gpr
+  - boringssl
diff --git a/gRPC.podspec b/gRPC.podspec
index 80f2681..97e13c0 100644
--- a/gRPC.podspec
+++ b/gRPC.podspec
@@ -396,6 +396,7 @@
                       'src/core/surface/server.c',
                       'src/core/surface/server_chttp2.c',
                       'src/core/surface/server_create.c',
+                      'src/core/surface/validate_metadata.c',
                       'src/core/surface/version.c',
                       'src/core/transport/byte_stream.c',
                       'src/core/transport/chttp2/alpn.c',
diff --git a/grpc.gemspec b/grpc.gemspec
new file mode 100755
index 0000000..4f3a3f2
--- /dev/null
+++ b/grpc.gemspec
@@ -0,0 +1,416 @@
+# -*- ruby -*-
+# encoding: utf-8
+$LOAD_PATH.push File.expand_path('../src/ruby/lib', __FILE__)
+require 'grpc/version'
+
+Gem::Specification.new do |s|
+  s.name          = 'grpc'
+  s.version       = GRPC::VERSION
+  s.authors       = ['gRPC Authors']
+  s.email         = 'temiola@google.com'
+  s.homepage      = 'https://github.com/google/grpc/tree/master/src/ruby'
+  s.summary       = 'GRPC system in Ruby'
+  s.description   = 'Send RPCs from Ruby using GRPC'
+  s.license       = 'BSD-3-Clause'
+
+  s.required_ruby_version = '>= 2.0.0'
+  s.requirements << 'libgrpc ~> 0.11.0 needs to be installed'
+
+  s.files = %w( Rakefile Makefile )
+  s.files += %w( etc/roots.pem )
+  s.files += Dir.glob('src/ruby/bin/**/*')
+  s.files += Dir.glob('src/ruby/ext/**/*')
+  s.files += Dir.glob('src/ruby/lib/**/*')
+  s.files += Dir.glob('src/ruby/pb/**/*')
+  s.files += Dir.glob('include/grpc/**/*')
+  s.test_files = Dir.glob('src/ruby/spec/**/*')
+  s.bindir = 'src/ruby/bin'
+  %w(math noproto).each do |b|
+    s.executables += ["#{b}_client.rb", "#{b}_server.rb"]
+  end
+  s.executables += %w(grpc_ruby_interop_client grpc_ruby_interop_server)
+  s.require_paths = %w( src/ruby/bin src/ruby/lib src/ruby/pb )
+  s.platform      = Gem::Platform::RUBY
+
+  s.add_dependency 'google-protobuf', '~> 3.0.0alpha.1.1'
+  s.add_dependency 'googleauth', '~> 0.5.1'
+
+  s.add_development_dependency 'bundler', '~> 1.9'
+  s.add_development_dependency 'logging', '~> 2.0'
+  s.add_development_dependency 'simplecov', '~> 0.9'
+  s.add_development_dependency 'rake', '~> 10.4'
+  s.add_development_dependency 'rake-compiler', '~> 0.9'
+  s.add_development_dependency 'rspec', '~> 3.2'
+  s.add_development_dependency 'rubocop', '~> 0.30.0'
+  s.add_development_dependency 'signet', '~>0.7.0'
+
+  s.extensions = %w(src/ruby/ext/grpc/extconf.rb)
+
+  s.files += %w( include/grpc/support/alloc.h )
+  s.files += %w( include/grpc/support/atm.h )
+  s.files += %w( include/grpc/support/atm_gcc_atomic.h )
+  s.files += %w( include/grpc/support/atm_gcc_sync.h )
+  s.files += %w( include/grpc/support/atm_win32.h )
+  s.files += %w( include/grpc/support/avl.h )
+  s.files += %w( include/grpc/support/cmdline.h )
+  s.files += %w( include/grpc/support/cpu.h )
+  s.files += %w( include/grpc/support/histogram.h )
+  s.files += %w( include/grpc/support/host_port.h )
+  s.files += %w( include/grpc/support/log.h )
+  s.files += %w( include/grpc/support/log_win32.h )
+  s.files += %w( include/grpc/support/port_platform.h )
+  s.files += %w( include/grpc/support/slice.h )
+  s.files += %w( include/grpc/support/slice_buffer.h )
+  s.files += %w( include/grpc/support/string_util.h )
+  s.files += %w( include/grpc/support/subprocess.h )
+  s.files += %w( include/grpc/support/sync.h )
+  s.files += %w( include/grpc/support/sync_generic.h )
+  s.files += %w( include/grpc/support/sync_posix.h )
+  s.files += %w( include/grpc/support/sync_win32.h )
+  s.files += %w( include/grpc/support/thd.h )
+  s.files += %w( include/grpc/support/time.h )
+  s.files += %w( include/grpc/support/tls.h )
+  s.files += %w( include/grpc/support/tls_gcc.h )
+  s.files += %w( include/grpc/support/tls_msvc.h )
+  s.files += %w( include/grpc/support/tls_pthread.h )
+  s.files += %w( include/grpc/support/useful.h )
+  s.files += %w( src/core/profiling/timers.h )
+  s.files += %w( src/core/support/block_annotate.h )
+  s.files += %w( src/core/support/env.h )
+  s.files += %w( src/core/support/file.h )
+  s.files += %w( src/core/support/murmur_hash.h )
+  s.files += %w( src/core/support/stack_lockfree.h )
+  s.files += %w( src/core/support/string.h )
+  s.files += %w( src/core/support/string_win32.h )
+  s.files += %w( src/core/support/thd_internal.h )
+  s.files += %w( src/core/support/time_precise.h )
+  s.files += %w( src/core/profiling/basic_timers.c )
+  s.files += %w( src/core/profiling/stap_timers.c )
+  s.files += %w( src/core/support/alloc.c )
+  s.files += %w( src/core/support/avl.c )
+  s.files += %w( src/core/support/cmdline.c )
+  s.files += %w( src/core/support/cpu_iphone.c )
+  s.files += %w( src/core/support/cpu_linux.c )
+  s.files += %w( src/core/support/cpu_posix.c )
+  s.files += %w( src/core/support/cpu_windows.c )
+  s.files += %w( src/core/support/env_linux.c )
+  s.files += %w( src/core/support/env_posix.c )
+  s.files += %w( src/core/support/env_win32.c )
+  s.files += %w( src/core/support/file.c )
+  s.files += %w( src/core/support/file_posix.c )
+  s.files += %w( src/core/support/file_win32.c )
+  s.files += %w( src/core/support/histogram.c )
+  s.files += %w( src/core/support/host_port.c )
+  s.files += %w( src/core/support/log.c )
+  s.files += %w( src/core/support/log_android.c )
+  s.files += %w( src/core/support/log_linux.c )
+  s.files += %w( src/core/support/log_posix.c )
+  s.files += %w( src/core/support/log_win32.c )
+  s.files += %w( src/core/support/murmur_hash.c )
+  s.files += %w( src/core/support/slice.c )
+  s.files += %w( src/core/support/slice_buffer.c )
+  s.files += %w( src/core/support/stack_lockfree.c )
+  s.files += %w( src/core/support/string.c )
+  s.files += %w( src/core/support/string_posix.c )
+  s.files += %w( src/core/support/string_win32.c )
+  s.files += %w( src/core/support/subprocess_posix.c )
+  s.files += %w( src/core/support/sync.c )
+  s.files += %w( src/core/support/sync_posix.c )
+  s.files += %w( src/core/support/sync_win32.c )
+  s.files += %w( src/core/support/thd.c )
+  s.files += %w( src/core/support/thd_posix.c )
+  s.files += %w( src/core/support/thd_win32.c )
+  s.files += %w( src/core/support/time.c )
+  s.files += %w( src/core/support/time_posix.c )
+  s.files += %w( src/core/support/time_precise.c )
+  s.files += %w( src/core/support/time_win32.c )
+  s.files += %w( src/core/support/tls_pthread.c )
+  s.files += %w( include/grpc/grpc_security.h )
+  s.files += %w( include/grpc/byte_buffer.h )
+  s.files += %w( include/grpc/byte_buffer_reader.h )
+  s.files += %w( include/grpc/compression.h )
+  s.files += %w( include/grpc/grpc.h )
+  s.files += %w( include/grpc/status.h )
+  s.files += %w( include/grpc/census.h )
+  s.files += %w( src/core/security/auth_filters.h )
+  s.files += %w( src/core/security/base64.h )
+  s.files += %w( src/core/security/credentials.h )
+  s.files += %w( src/core/security/handshake.h )
+  s.files += %w( src/core/security/json_token.h )
+  s.files += %w( src/core/security/jwt_verifier.h )
+  s.files += %w( src/core/security/secure_endpoint.h )
+  s.files += %w( src/core/security/security_connector.h )
+  s.files += %w( src/core/security/security_context.h )
+  s.files += %w( src/core/tsi/fake_transport_security.h )
+  s.files += %w( src/core/tsi/ssl_transport_security.h )
+  s.files += %w( src/core/tsi/ssl_types.h )
+  s.files += %w( src/core/tsi/transport_security.h )
+  s.files += %w( src/core/tsi/transport_security_interface.h )
+  s.files += %w( src/core/census/grpc_filter.h )
+  s.files += %w( src/core/channel/channel_args.h )
+  s.files += %w( src/core/channel/channel_stack.h )
+  s.files += %w( src/core/channel/client_channel.h )
+  s.files += %w( src/core/channel/client_uchannel.h )
+  s.files += %w( src/core/channel/compress_filter.h )
+  s.files += %w( src/core/channel/connected_channel.h )
+  s.files += %w( src/core/channel/context.h )
+  s.files += %w( src/core/channel/http_client_filter.h )
+  s.files += %w( src/core/channel/http_server_filter.h )
+  s.files += %w( src/core/channel/subchannel_call_holder.h )
+  s.files += %w( src/core/client_config/client_config.h )
+  s.files += %w( src/core/client_config/connector.h )
+  s.files += %w( src/core/client_config/initial_connect_string.h )
+  s.files += %w( src/core/client_config/lb_policies/pick_first.h )
+  s.files += %w( src/core/client_config/lb_policies/round_robin.h )
+  s.files += %w( src/core/client_config/lb_policy.h )
+  s.files += %w( src/core/client_config/lb_policy_factory.h )
+  s.files += %w( src/core/client_config/lb_policy_registry.h )
+  s.files += %w( src/core/client_config/resolver.h )
+  s.files += %w( src/core/client_config/resolver_factory.h )
+  s.files += %w( src/core/client_config/resolver_registry.h )
+  s.files += %w( src/core/client_config/resolvers/dns_resolver.h )
+  s.files += %w( src/core/client_config/resolvers/sockaddr_resolver.h )
+  s.files += %w( src/core/client_config/subchannel.h )
+  s.files += %w( src/core/client_config/subchannel_factory.h )
+  s.files += %w( src/core/client_config/uri_parser.h )
+  s.files += %w( src/core/compression/algorithm_metadata.h )
+  s.files += %w( src/core/compression/message_compress.h )
+  s.files += %w( src/core/debug/trace.h )
+  s.files += %w( src/core/httpcli/format_request.h )
+  s.files += %w( src/core/httpcli/httpcli.h )
+  s.files += %w( src/core/httpcli/parser.h )
+  s.files += %w( src/core/iomgr/closure.h )
+  s.files += %w( src/core/iomgr/endpoint.h )
+  s.files += %w( src/core/iomgr/endpoint_pair.h )
+  s.files += %w( src/core/iomgr/exec_ctx.h )
+  s.files += %w( src/core/iomgr/executor.h )
+  s.files += %w( src/core/iomgr/fd_posix.h )
+  s.files += %w( src/core/iomgr/iocp_windows.h )
+  s.files += %w( src/core/iomgr/iomgr.h )
+  s.files += %w( src/core/iomgr/iomgr_internal.h )
+  s.files += %w( src/core/iomgr/iomgr_posix.h )
+  s.files += %w( src/core/iomgr/pollset.h )
+  s.files += %w( src/core/iomgr/pollset_posix.h )
+  s.files += %w( src/core/iomgr/pollset_set.h )
+  s.files += %w( src/core/iomgr/pollset_set_posix.h )
+  s.files += %w( src/core/iomgr/pollset_set_windows.h )
+  s.files += %w( src/core/iomgr/pollset_windows.h )
+  s.files += %w( src/core/iomgr/resolve_address.h )
+  s.files += %w( src/core/iomgr/sockaddr.h )
+  s.files += %w( src/core/iomgr/sockaddr_posix.h )
+  s.files += %w( src/core/iomgr/sockaddr_utils.h )
+  s.files += %w( src/core/iomgr/sockaddr_win32.h )
+  s.files += %w( src/core/iomgr/socket_utils_posix.h )
+  s.files += %w( src/core/iomgr/socket_windows.h )
+  s.files += %w( src/core/iomgr/tcp_client.h )
+  s.files += %w( src/core/iomgr/tcp_posix.h )
+  s.files += %w( src/core/iomgr/tcp_server.h )
+  s.files += %w( src/core/iomgr/tcp_windows.h )
+  s.files += %w( src/core/iomgr/time_averaged_stats.h )
+  s.files += %w( src/core/iomgr/timer.h )
+  s.files += %w( src/core/iomgr/timer_heap.h )
+  s.files += %w( src/core/iomgr/timer_internal.h )
+  s.files += %w( src/core/iomgr/udp_server.h )
+  s.files += %w( src/core/iomgr/wakeup_fd_pipe.h )
+  s.files += %w( src/core/iomgr/wakeup_fd_posix.h )
+  s.files += %w( src/core/iomgr/workqueue.h )
+  s.files += %w( src/core/iomgr/workqueue_posix.h )
+  s.files += %w( src/core/iomgr/workqueue_windows.h )
+  s.files += %w( src/core/json/json.h )
+  s.files += %w( src/core/json/json_common.h )
+  s.files += %w( src/core/json/json_reader.h )
+  s.files += %w( src/core/json/json_writer.h )
+  s.files += %w( src/core/statistics/census_interface.h )
+  s.files += %w( src/core/statistics/census_rpc_stats.h )
+  s.files += %w( src/core/surface/api_trace.h )
+  s.files += %w( src/core/surface/call.h )
+  s.files += %w( src/core/surface/call_test_only.h )
+  s.files += %w( src/core/surface/channel.h )
+  s.files += %w( src/core/surface/completion_queue.h )
+  s.files += %w( src/core/surface/event_string.h )
+  s.files += %w( src/core/surface/init.h )
+  s.files += %w( src/core/surface/server.h )
+  s.files += %w( src/core/surface/surface_trace.h )
+  s.files += %w( src/core/transport/byte_stream.h )
+  s.files += %w( src/core/transport/chttp2/alpn.h )
+  s.files += %w( src/core/transport/chttp2/bin_encoder.h )
+  s.files += %w( src/core/transport/chttp2/frame.h )
+  s.files += %w( src/core/transport/chttp2/frame_data.h )
+  s.files += %w( src/core/transport/chttp2/frame_goaway.h )
+  s.files += %w( src/core/transport/chttp2/frame_ping.h )
+  s.files += %w( src/core/transport/chttp2/frame_rst_stream.h )
+  s.files += %w( src/core/transport/chttp2/frame_settings.h )
+  s.files += %w( src/core/transport/chttp2/frame_window_update.h )
+  s.files += %w( src/core/transport/chttp2/hpack_encoder.h )
+  s.files += %w( src/core/transport/chttp2/hpack_parser.h )
+  s.files += %w( src/core/transport/chttp2/hpack_table.h )
+  s.files += %w( src/core/transport/chttp2/http2_errors.h )
+  s.files += %w( src/core/transport/chttp2/huffsyms.h )
+  s.files += %w( src/core/transport/chttp2/incoming_metadata.h )
+  s.files += %w( src/core/transport/chttp2/internal.h )
+  s.files += %w( src/core/transport/chttp2/status_conversion.h )
+  s.files += %w( src/core/transport/chttp2/stream_map.h )
+  s.files += %w( src/core/transport/chttp2/timeout_encoding.h )
+  s.files += %w( src/core/transport/chttp2/varint.h )
+  s.files += %w( src/core/transport/chttp2_transport.h )
+  s.files += %w( src/core/transport/connectivity_state.h )
+  s.files += %w( src/core/transport/metadata.h )
+  s.files += %w( src/core/transport/metadata_batch.h )
+  s.files += %w( src/core/transport/static_metadata.h )
+  s.files += %w( src/core/transport/transport.h )
+  s.files += %w( src/core/transport/transport_impl.h )
+  s.files += %w( src/core/census/aggregation.h )
+  s.files += %w( src/core/census/context.h )
+  s.files += %w( src/core/census/rpc_metric_id.h )
+  s.files += %w( src/core/httpcli/httpcli_security_connector.c )
+  s.files += %w( src/core/security/base64.c )
+  s.files += %w( src/core/security/client_auth_filter.c )
+  s.files += %w( src/core/security/credentials.c )
+  s.files += %w( src/core/security/credentials_metadata.c )
+  s.files += %w( src/core/security/credentials_posix.c )
+  s.files += %w( src/core/security/credentials_win32.c )
+  s.files += %w( src/core/security/google_default_credentials.c )
+  s.files += %w( src/core/security/handshake.c )
+  s.files += %w( src/core/security/json_token.c )
+  s.files += %w( src/core/security/jwt_verifier.c )
+  s.files += %w( src/core/security/secure_endpoint.c )
+  s.files += %w( src/core/security/security_connector.c )
+  s.files += %w( src/core/security/security_context.c )
+  s.files += %w( src/core/security/server_auth_filter.c )
+  s.files += %w( src/core/security/server_secure_chttp2.c )
+  s.files += %w( src/core/surface/init_secure.c )
+  s.files += %w( src/core/surface/secure_channel_create.c )
+  s.files += %w( src/core/tsi/fake_transport_security.c )
+  s.files += %w( src/core/tsi/ssl_transport_security.c )
+  s.files += %w( src/core/tsi/transport_security.c )
+  s.files += %w( src/core/census/grpc_context.c )
+  s.files += %w( src/core/census/grpc_filter.c )
+  s.files += %w( src/core/channel/channel_args.c )
+  s.files += %w( src/core/channel/channel_stack.c )
+  s.files += %w( src/core/channel/client_channel.c )
+  s.files += %w( src/core/channel/client_uchannel.c )
+  s.files += %w( src/core/channel/compress_filter.c )
+  s.files += %w( src/core/channel/connected_channel.c )
+  s.files += %w( src/core/channel/http_client_filter.c )
+  s.files += %w( src/core/channel/http_server_filter.c )
+  s.files += %w( src/core/channel/subchannel_call_holder.c )
+  s.files += %w( src/core/client_config/client_config.c )
+  s.files += %w( src/core/client_config/connector.c )
+  s.files += %w( src/core/client_config/default_initial_connect_string.c )
+  s.files += %w( src/core/client_config/initial_connect_string.c )
+  s.files += %w( src/core/client_config/lb_policies/pick_first.c )
+  s.files += %w( src/core/client_config/lb_policies/round_robin.c )
+  s.files += %w( src/core/client_config/lb_policy.c )
+  s.files += %w( src/core/client_config/lb_policy_factory.c )
+  s.files += %w( src/core/client_config/lb_policy_registry.c )
+  s.files += %w( src/core/client_config/resolver.c )
+  s.files += %w( src/core/client_config/resolver_factory.c )
+  s.files += %w( src/core/client_config/resolver_registry.c )
+  s.files += %w( src/core/client_config/resolvers/dns_resolver.c )
+  s.files += %w( src/core/client_config/resolvers/sockaddr_resolver.c )
+  s.files += %w( src/core/client_config/subchannel.c )
+  s.files += %w( src/core/client_config/subchannel_factory.c )
+  s.files += %w( src/core/client_config/uri_parser.c )
+  s.files += %w( src/core/compression/algorithm.c )
+  s.files += %w( src/core/compression/message_compress.c )
+  s.files += %w( src/core/debug/trace.c )
+  s.files += %w( src/core/httpcli/format_request.c )
+  s.files += %w( src/core/httpcli/httpcli.c )
+  s.files += %w( src/core/httpcli/parser.c )
+  s.files += %w( src/core/iomgr/closure.c )
+  s.files += %w( src/core/iomgr/endpoint.c )
+  s.files += %w( src/core/iomgr/endpoint_pair_posix.c )
+  s.files += %w( src/core/iomgr/endpoint_pair_windows.c )
+  s.files += %w( src/core/iomgr/exec_ctx.c )
+  s.files += %w( src/core/iomgr/executor.c )
+  s.files += %w( src/core/iomgr/fd_posix.c )
+  s.files += %w( src/core/iomgr/iocp_windows.c )
+  s.files += %w( src/core/iomgr/iomgr.c )
+  s.files += %w( src/core/iomgr/iomgr_posix.c )
+  s.files += %w( src/core/iomgr/iomgr_windows.c )
+  s.files += %w( src/core/iomgr/pollset_multipoller_with_epoll.c )
+  s.files += %w( src/core/iomgr/pollset_multipoller_with_poll_posix.c )
+  s.files += %w( src/core/iomgr/pollset_posix.c )
+  s.files += %w( src/core/iomgr/pollset_set_posix.c )
+  s.files += %w( src/core/iomgr/pollset_set_windows.c )
+  s.files += %w( src/core/iomgr/pollset_windows.c )
+  s.files += %w( src/core/iomgr/resolve_address_posix.c )
+  s.files += %w( src/core/iomgr/resolve_address_windows.c )
+  s.files += %w( src/core/iomgr/sockaddr_utils.c )
+  s.files += %w( src/core/iomgr/socket_utils_common_posix.c )
+  s.files += %w( src/core/iomgr/socket_utils_linux.c )
+  s.files += %w( src/core/iomgr/socket_utils_posix.c )
+  s.files += %w( src/core/iomgr/socket_windows.c )
+  s.files += %w( src/core/iomgr/tcp_client_posix.c )
+  s.files += %w( src/core/iomgr/tcp_client_windows.c )
+  s.files += %w( src/core/iomgr/tcp_posix.c )
+  s.files += %w( src/core/iomgr/tcp_server_posix.c )
+  s.files += %w( src/core/iomgr/tcp_server_windows.c )
+  s.files += %w( src/core/iomgr/tcp_windows.c )
+  s.files += %w( src/core/iomgr/time_averaged_stats.c )
+  s.files += %w( src/core/iomgr/timer.c )
+  s.files += %w( src/core/iomgr/timer_heap.c )
+  s.files += %w( src/core/iomgr/udp_server.c )
+  s.files += %w( src/core/iomgr/wakeup_fd_eventfd.c )
+  s.files += %w( src/core/iomgr/wakeup_fd_nospecial.c )
+  s.files += %w( src/core/iomgr/wakeup_fd_pipe.c )
+  s.files += %w( src/core/iomgr/wakeup_fd_posix.c )
+  s.files += %w( src/core/iomgr/workqueue_posix.c )
+  s.files += %w( src/core/iomgr/workqueue_windows.c )
+  s.files += %w( src/core/json/json.c )
+  s.files += %w( src/core/json/json_reader.c )
+  s.files += %w( src/core/json/json_string.c )
+  s.files += %w( src/core/json/json_writer.c )
+  s.files += %w( src/core/surface/api_trace.c )
+  s.files += %w( src/core/surface/byte_buffer.c )
+  s.files += %w( src/core/surface/byte_buffer_reader.c )
+  s.files += %w( src/core/surface/call.c )
+  s.files += %w( src/core/surface/call_details.c )
+  s.files += %w( src/core/surface/call_log_batch.c )
+  s.files += %w( src/core/surface/channel.c )
+  s.files += %w( src/core/surface/channel_connectivity.c )
+  s.files += %w( src/core/surface/channel_create.c )
+  s.files += %w( src/core/surface/channel_ping.c )
+  s.files += %w( src/core/surface/completion_queue.c )
+  s.files += %w( src/core/surface/event_string.c )
+  s.files += %w( src/core/surface/init.c )
+  s.files += %w( src/core/surface/lame_client.c )
+  s.files += %w( src/core/surface/metadata_array.c )
+  s.files += %w( src/core/surface/server.c )
+  s.files += %w( src/core/surface/server_chttp2.c )
+  s.files += %w( src/core/surface/server_create.c )
+  s.files += %w( src/core/surface/validate_metadata.c )
+  s.files += %w( src/core/surface/version.c )
+  s.files += %w( src/core/transport/byte_stream.c )
+  s.files += %w( src/core/transport/chttp2/alpn.c )
+  s.files += %w( src/core/transport/chttp2/bin_encoder.c )
+  s.files += %w( src/core/transport/chttp2/frame_data.c )
+  s.files += %w( src/core/transport/chttp2/frame_goaway.c )
+  s.files += %w( src/core/transport/chttp2/frame_ping.c )
+  s.files += %w( src/core/transport/chttp2/frame_rst_stream.c )
+  s.files += %w( src/core/transport/chttp2/frame_settings.c )
+  s.files += %w( src/core/transport/chttp2/frame_window_update.c )
+  s.files += %w( src/core/transport/chttp2/hpack_encoder.c )
+  s.files += %w( src/core/transport/chttp2/hpack_parser.c )
+  s.files += %w( src/core/transport/chttp2/hpack_table.c )
+  s.files += %w( src/core/transport/chttp2/huffsyms.c )
+  s.files += %w( src/core/transport/chttp2/incoming_metadata.c )
+  s.files += %w( src/core/transport/chttp2/parsing.c )
+  s.files += %w( src/core/transport/chttp2/status_conversion.c )
+  s.files += %w( src/core/transport/chttp2/stream_lists.c )
+  s.files += %w( src/core/transport/chttp2/stream_map.c )
+  s.files += %w( src/core/transport/chttp2/timeout_encoding.c )
+  s.files += %w( src/core/transport/chttp2/varint.c )
+  s.files += %w( src/core/transport/chttp2/writing.c )
+  s.files += %w( src/core/transport/chttp2_transport.c )
+  s.files += %w( src/core/transport/connectivity_state.c )
+  s.files += %w( src/core/transport/metadata.c )
+  s.files += %w( src/core/transport/metadata_batch.c )
+  s.files += %w( src/core/transport/static_metadata.c )
+  s.files += %w( src/core/transport/transport.c )
+  s.files += %w( src/core/transport/transport_op_string.c )
+  s.files += %w( src/core/census/context.c )
+  s.files += %w( src/core/census/initialize.c )
+  s.files += %w( src/core/census/operation.c )
+  s.files += %w( src/core/census/tracing.c )
+end
diff --git a/include/grpc++/channel.h b/include/grpc++/channel.h
index de3ebfe..541be13 100644
--- a/include/grpc++/channel.h
+++ b/include/grpc++/channel.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/include/grpc++/client_context.h b/include/grpc++/client_context.h
index 24f80ab..25eeb38 100644
--- a/include/grpc++/client_context.h
+++ b/include/grpc++/client_context.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -244,7 +244,7 @@
   /// client’s identity, role, or whether it is authorized to make a particular
   /// call.
   ///
-  /// \see  https://github.com/grpc/grpc/blob/master/doc/grpc-auth-support.md
+  /// \see  http://www.grpc.io/docs/guides/auth.html
   void set_credentials(const std::shared_ptr<CallCredentials>& creds) {
     creds_ = creds;
   }
diff --git a/include/grpc++/completion_queue.h b/include/grpc++/completion_queue.h
index b479239..5c2bc20 100644
--- a/include/grpc++/completion_queue.h
+++ b/include/grpc++/completion_queue.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/include/grpc++/generic/async_generic_service.h b/include/grpc++/generic/async_generic_service.h
index 8578d85..57a2696 100644
--- a/include/grpc++/generic/async_generic_service.h
+++ b/include/grpc++/generic/async_generic_service.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -61,6 +61,7 @@
   // TODO(yangg) Once we can add multiple completion queues to the server
   // in c core, add a CompletionQueue* argument to the ctor here.
   // TODO(yangg) support methods list.
+  AsyncGenericService() : server_(nullptr) {}
   AsyncGenericService(const grpc::string& methods) : server_(nullptr) {}
 
   void RequestCall(GenericServerContext* ctx,
diff --git a/include/grpc++/security/credentials.h b/include/grpc++/security/credentials.h
index a06dcf1..75945fd 100644
--- a/include/grpc++/security/credentials.h
+++ b/include/grpc++/security/credentials.h
@@ -55,7 +55,7 @@
 /// It can make various assertions, e.g., about the client’s identity, role
 /// for all the calls on that channel.
 ///
-/// \see https://github.com/grpc/grpc/blob/master/doc/grpc-auth-support.md
+/// \see http://www.grpc.io/docs/guides/auth.html
 class ChannelCredentials : public GrpcLibrary {
  public:
   ~ChannelCredentials() GRPC_OVERRIDE;
diff --git a/include/grpc++/server_context.h b/include/grpc++/server_context.h
index abb3e5a..8ba7348 100644
--- a/include/grpc++/server_context.h
+++ b/include/grpc++/server_context.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/include/grpc++/support/async_stream.h b/include/grpc++/support/async_stream.h
index 823fcd2..0c96352 100644
--- a/include/grpc++/support/async_stream.h
+++ b/include/grpc++/support/async_stream.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/include/grpc++/support/byte_buffer.h b/include/grpc++/support/byte_buffer.h
index 9d19b07..82591a8 100644
--- a/include/grpc++/support/byte_buffer.h
+++ b/include/grpc++/support/byte_buffer.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -55,8 +55,14 @@
   /// Construct buffer from \a slices, of which there are \a nslices.
   ByteBuffer(const Slice* slices, size_t nslices);
 
+  /// Constuct a byte buffer by referencing elements of existing buffer
+  /// \a buf. Wrapper of core function grpc_byte_buffer_copy
+  ByteBuffer(const ByteBuffer& buf);
+
   ~ByteBuffer();
 
+  ByteBuffer& operator=(const ByteBuffer&);
+
   /// Dump (read) the buffer contents into \a slices.
   void Dump(std::vector<Slice>* slices) const;
 
@@ -69,9 +75,6 @@
  private:
   friend class SerializationTraits<ByteBuffer, void>;
 
-  ByteBuffer(const ByteBuffer&);
-  ByteBuffer& operator=(const ByteBuffer&);
-
   // takes ownership
   void set_buffer(grpc_byte_buffer* buf) {
     if (buffer_) {
diff --git a/include/grpc++/support/slice.h b/include/grpc++/support/slice.h
index be316e7..724691a 100644
--- a/include/grpc++/support/slice.h
+++ b/include/grpc++/support/slice.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -39,7 +39,7 @@
 
 namespace grpc {
 
-/// A wrapper around \a grpc_slice.
+/// A wrapper around \a gpr_slice.
 ///
 /// A slice represents a contiguous reference counted array of bytes.
 /// It is cheap to take references to a slice, and it is cheap to create a
diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h
index be4c4d2..952e86e 100644
--- a/include/grpc/grpc.h
+++ b/include/grpc/grpc.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -715,6 +715,16 @@
     thread-safety issues raised by it should not be of concern. */
 int grpc_tracer_set_enabled(const char *name, int enabled);
 
+/** Check whether a metadata key is legal (will be accepted by core) */
+int grpc_header_key_is_legal(const char *key, size_t length);
+
+/** Check whether a non-binary metadata value is legal (will be accepted by
+    core) */
+int grpc_header_nonbin_value_is_legal(const char *value, size_t length);
+
+/** Check whether a metadata key corresponds to a binary value */
+int grpc_is_binary_header(const char *key, size_t length);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/package.json b/package.json
index f39dfc4..e0bbb7d 100644
--- a/package.json
+++ b/package.json
@@ -31,13 +31,13 @@
     "protobufjs": "^4.0.0"
   },
   "devDependencies": {
-    "async": "^0.9.0",
+    "async": "^1.5.0",
     "google-auth-library": "^0.9.2",
     "istanbul": "^0.3.21",
     "jsdoc": "^3.3.2",
     "jshint": "^2.5.0",
     "minimist": "^1.1.0",
-    "mocha": "~1.21.0",
+    "mocha": "^2.3.4",
     "mocha-jenkins-reporter": "^0.1.9",
     "mustache": "^2.0.0",
     "poisson-process": "^0.2.1"
@@ -48,14 +48,401 @@
   "files": [
     "LICENSE",
     "src/node/README.md",
-    "src/node/index.js",
-    "src/node/ext",
     "src/node/health_check",
-    "src/node/src",
-    "src/core",
-    "test/proto",
-    "include",
+    "src/proto",
     "etc",
+    "src/node/ext/byte_buffer.h",
+    "src/node/ext/call.h",
+    "src/node/ext/call_credentials.h",
+    "src/node/ext/channel.h",
+    "src/node/ext/channel_credentials.h",
+    "src/node/ext/completion_queue_async_worker.h",
+    "src/node/ext/server.h",
+    "src/node/ext/server_credentials.h",
+    "src/node/ext/timeval.h",
+    "src/node/ext/byte_buffer.cc",
+    "src/node/ext/call.cc",
+    "src/node/ext/call_credentials.cc",
+    "src/node/ext/channel.cc",
+    "src/node/ext/channel_credentials.cc",
+    "src/node/ext/completion_queue_async_worker.cc",
+    "src/node/ext/node_grpc.cc",
+    "src/node/ext/server.cc",
+    "src/node/ext/server_credentials.cc",
+    "src/node/ext/timeval.cc",
+    "src/node/index.js",
+    "src/node/src/client.js",
+    "src/node/src/common.js",
+    "src/node/src/credentials.js",
+    "src/node/src/metadata.js",
+    "src/node/src/server.js",
+    "include/grpc/grpc_security.h",
+    "include/grpc/byte_buffer.h",
+    "include/grpc/byte_buffer_reader.h",
+    "include/grpc/compression.h",
+    "include/grpc/grpc.h",
+    "include/grpc/status.h",
+    "include/grpc/census.h",
+    "src/core/security/auth_filters.h",
+    "src/core/security/base64.h",
+    "src/core/security/credentials.h",
+    "src/core/security/handshake.h",
+    "src/core/security/json_token.h",
+    "src/core/security/jwt_verifier.h",
+    "src/core/security/secure_endpoint.h",
+    "src/core/security/security_connector.h",
+    "src/core/security/security_context.h",
+    "src/core/tsi/fake_transport_security.h",
+    "src/core/tsi/ssl_transport_security.h",
+    "src/core/tsi/ssl_types.h",
+    "src/core/tsi/transport_security.h",
+    "src/core/tsi/transport_security_interface.h",
+    "src/core/census/grpc_filter.h",
+    "src/core/channel/channel_args.h",
+    "src/core/channel/channel_stack.h",
+    "src/core/channel/client_channel.h",
+    "src/core/channel/client_uchannel.h",
+    "src/core/channel/compress_filter.h",
+    "src/core/channel/connected_channel.h",
+    "src/core/channel/context.h",
+    "src/core/channel/http_client_filter.h",
+    "src/core/channel/http_server_filter.h",
+    "src/core/channel/subchannel_call_holder.h",
+    "src/core/client_config/client_config.h",
+    "src/core/client_config/connector.h",
+    "src/core/client_config/initial_connect_string.h",
+    "src/core/client_config/lb_policies/pick_first.h",
+    "src/core/client_config/lb_policies/round_robin.h",
+    "src/core/client_config/lb_policy.h",
+    "src/core/client_config/lb_policy_factory.h",
+    "src/core/client_config/lb_policy_registry.h",
+    "src/core/client_config/resolver.h",
+    "src/core/client_config/resolver_factory.h",
+    "src/core/client_config/resolver_registry.h",
+    "src/core/client_config/resolvers/dns_resolver.h",
+    "src/core/client_config/resolvers/sockaddr_resolver.h",
+    "src/core/client_config/subchannel.h",
+    "src/core/client_config/subchannel_factory.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",
+    "src/core/httpcli/httpcli.h",
+    "src/core/httpcli/parser.h",
+    "src/core/iomgr/closure.h",
+    "src/core/iomgr/endpoint.h",
+    "src/core/iomgr/endpoint_pair.h",
+    "src/core/iomgr/exec_ctx.h",
+    "src/core/iomgr/executor.h",
+    "src/core/iomgr/fd_posix.h",
+    "src/core/iomgr/iocp_windows.h",
+    "src/core/iomgr/iomgr.h",
+    "src/core/iomgr/iomgr_internal.h",
+    "src/core/iomgr/iomgr_posix.h",
+    "src/core/iomgr/pollset.h",
+    "src/core/iomgr/pollset_posix.h",
+    "src/core/iomgr/pollset_set.h",
+    "src/core/iomgr/pollset_set_posix.h",
+    "src/core/iomgr/pollset_set_windows.h",
+    "src/core/iomgr/pollset_windows.h",
+    "src/core/iomgr/resolve_address.h",
+    "src/core/iomgr/sockaddr.h",
+    "src/core/iomgr/sockaddr_posix.h",
+    "src/core/iomgr/sockaddr_utils.h",
+    "src/core/iomgr/sockaddr_win32.h",
+    "src/core/iomgr/socket_utils_posix.h",
+    "src/core/iomgr/socket_windows.h",
+    "src/core/iomgr/tcp_client.h",
+    "src/core/iomgr/tcp_posix.h",
+    "src/core/iomgr/tcp_server.h",
+    "src/core/iomgr/tcp_windows.h",
+    "src/core/iomgr/time_averaged_stats.h",
+    "src/core/iomgr/timer.h",
+    "src/core/iomgr/timer_heap.h",
+    "src/core/iomgr/timer_internal.h",
+    "src/core/iomgr/udp_server.h",
+    "src/core/iomgr/wakeup_fd_pipe.h",
+    "src/core/iomgr/wakeup_fd_posix.h",
+    "src/core/iomgr/workqueue.h",
+    "src/core/iomgr/workqueue_posix.h",
+    "src/core/iomgr/workqueue_windows.h",
+    "src/core/json/json.h",
+    "src/core/json/json_common.h",
+    "src/core/json/json_reader.h",
+    "src/core/json/json_writer.h",
+    "src/core/statistics/census_interface.h",
+    "src/core/statistics/census_rpc_stats.h",
+    "src/core/surface/api_trace.h",
+    "src/core/surface/call.h",
+    "src/core/surface/call_test_only.h",
+    "src/core/surface/channel.h",
+    "src/core/surface/completion_queue.h",
+    "src/core/surface/event_string.h",
+    "src/core/surface/init.h",
+    "src/core/surface/server.h",
+    "src/core/surface/surface_trace.h",
+    "src/core/transport/byte_stream.h",
+    "src/core/transport/chttp2/alpn.h",
+    "src/core/transport/chttp2/bin_encoder.h",
+    "src/core/transport/chttp2/frame.h",
+    "src/core/transport/chttp2/frame_data.h",
+    "src/core/transport/chttp2/frame_goaway.h",
+    "src/core/transport/chttp2/frame_ping.h",
+    "src/core/transport/chttp2/frame_rst_stream.h",
+    "src/core/transport/chttp2/frame_settings.h",
+    "src/core/transport/chttp2/frame_window_update.h",
+    "src/core/transport/chttp2/hpack_encoder.h",
+    "src/core/transport/chttp2/hpack_parser.h",
+    "src/core/transport/chttp2/hpack_table.h",
+    "src/core/transport/chttp2/http2_errors.h",
+    "src/core/transport/chttp2/huffsyms.h",
+    "src/core/transport/chttp2/incoming_metadata.h",
+    "src/core/transport/chttp2/internal.h",
+    "src/core/transport/chttp2/status_conversion.h",
+    "src/core/transport/chttp2/stream_map.h",
+    "src/core/transport/chttp2/timeout_encoding.h",
+    "src/core/transport/chttp2/varint.h",
+    "src/core/transport/chttp2_transport.h",
+    "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",
+    "src/core/census/context.h",
+    "src/core/census/rpc_metric_id.h",
+    "src/core/httpcli/httpcli_security_connector.c",
+    "src/core/security/base64.c",
+    "src/core/security/client_auth_filter.c",
+    "src/core/security/credentials.c",
+    "src/core/security/credentials_metadata.c",
+    "src/core/security/credentials_posix.c",
+    "src/core/security/credentials_win32.c",
+    "src/core/security/google_default_credentials.c",
+    "src/core/security/handshake.c",
+    "src/core/security/json_token.c",
+    "src/core/security/jwt_verifier.c",
+    "src/core/security/secure_endpoint.c",
+    "src/core/security/security_connector.c",
+    "src/core/security/security_context.c",
+    "src/core/security/server_auth_filter.c",
+    "src/core/security/server_secure_chttp2.c",
+    "src/core/surface/init_secure.c",
+    "src/core/surface/secure_channel_create.c",
+    "src/core/tsi/fake_transport_security.c",
+    "src/core/tsi/ssl_transport_security.c",
+    "src/core/tsi/transport_security.c",
+    "src/core/census/grpc_context.c",
+    "src/core/census/grpc_filter.c",
+    "src/core/channel/channel_args.c",
+    "src/core/channel/channel_stack.c",
+    "src/core/channel/client_channel.c",
+    "src/core/channel/client_uchannel.c",
+    "src/core/channel/compress_filter.c",
+    "src/core/channel/connected_channel.c",
+    "src/core/channel/http_client_filter.c",
+    "src/core/channel/http_server_filter.c",
+    "src/core/channel/subchannel_call_holder.c",
+    "src/core/client_config/client_config.c",
+    "src/core/client_config/connector.c",
+    "src/core/client_config/default_initial_connect_string.c",
+    "src/core/client_config/initial_connect_string.c",
+    "src/core/client_config/lb_policies/pick_first.c",
+    "src/core/client_config/lb_policies/round_robin.c",
+    "src/core/client_config/lb_policy.c",
+    "src/core/client_config/lb_policy_factory.c",
+    "src/core/client_config/lb_policy_registry.c",
+    "src/core/client_config/resolver.c",
+    "src/core/client_config/resolver_factory.c",
+    "src/core/client_config/resolver_registry.c",
+    "src/core/client_config/resolvers/dns_resolver.c",
+    "src/core/client_config/resolvers/sockaddr_resolver.c",
+    "src/core/client_config/subchannel.c",
+    "src/core/client_config/subchannel_factory.c",
+    "src/core/client_config/uri_parser.c",
+    "src/core/compression/algorithm.c",
+    "src/core/compression/message_compress.c",
+    "src/core/debug/trace.c",
+    "src/core/httpcli/format_request.c",
+    "src/core/httpcli/httpcli.c",
+    "src/core/httpcli/parser.c",
+    "src/core/iomgr/closure.c",
+    "src/core/iomgr/endpoint.c",
+    "src/core/iomgr/endpoint_pair_posix.c",
+    "src/core/iomgr/endpoint_pair_windows.c",
+    "src/core/iomgr/exec_ctx.c",
+    "src/core/iomgr/executor.c",
+    "src/core/iomgr/fd_posix.c",
+    "src/core/iomgr/iocp_windows.c",
+    "src/core/iomgr/iomgr.c",
+    "src/core/iomgr/iomgr_posix.c",
+    "src/core/iomgr/iomgr_windows.c",
+    "src/core/iomgr/pollset_multipoller_with_epoll.c",
+    "src/core/iomgr/pollset_multipoller_with_poll_posix.c",
+    "src/core/iomgr/pollset_posix.c",
+    "src/core/iomgr/pollset_set_posix.c",
+    "src/core/iomgr/pollset_set_windows.c",
+    "src/core/iomgr/pollset_windows.c",
+    "src/core/iomgr/resolve_address_posix.c",
+    "src/core/iomgr/resolve_address_windows.c",
+    "src/core/iomgr/sockaddr_utils.c",
+    "src/core/iomgr/socket_utils_common_posix.c",
+    "src/core/iomgr/socket_utils_linux.c",
+    "src/core/iomgr/socket_utils_posix.c",
+    "src/core/iomgr/socket_windows.c",
+    "src/core/iomgr/tcp_client_posix.c",
+    "src/core/iomgr/tcp_client_windows.c",
+    "src/core/iomgr/tcp_posix.c",
+    "src/core/iomgr/tcp_server_posix.c",
+    "src/core/iomgr/tcp_server_windows.c",
+    "src/core/iomgr/tcp_windows.c",
+    "src/core/iomgr/time_averaged_stats.c",
+    "src/core/iomgr/timer.c",
+    "src/core/iomgr/timer_heap.c",
+    "src/core/iomgr/udp_server.c",
+    "src/core/iomgr/wakeup_fd_eventfd.c",
+    "src/core/iomgr/wakeup_fd_nospecial.c",
+    "src/core/iomgr/wakeup_fd_pipe.c",
+    "src/core/iomgr/wakeup_fd_posix.c",
+    "src/core/iomgr/workqueue_posix.c",
+    "src/core/iomgr/workqueue_windows.c",
+    "src/core/json/json.c",
+    "src/core/json/json_reader.c",
+    "src/core/json/json_string.c",
+    "src/core/json/json_writer.c",
+    "src/core/surface/api_trace.c",
+    "src/core/surface/byte_buffer.c",
+    "src/core/surface/byte_buffer_reader.c",
+    "src/core/surface/call.c",
+    "src/core/surface/call_details.c",
+    "src/core/surface/call_log_batch.c",
+    "src/core/surface/channel.c",
+    "src/core/surface/channel_connectivity.c",
+    "src/core/surface/channel_create.c",
+    "src/core/surface/channel_ping.c",
+    "src/core/surface/completion_queue.c",
+    "src/core/surface/event_string.c",
+    "src/core/surface/init.c",
+    "src/core/surface/lame_client.c",
+    "src/core/surface/metadata_array.c",
+    "src/core/surface/server.c",
+    "src/core/surface/server_chttp2.c",
+    "src/core/surface/server_create.c",
+    "src/core/surface/validate_metadata.c",
+    "src/core/surface/version.c",
+    "src/core/transport/byte_stream.c",
+    "src/core/transport/chttp2/alpn.c",
+    "src/core/transport/chttp2/bin_encoder.c",
+    "src/core/transport/chttp2/frame_data.c",
+    "src/core/transport/chttp2/frame_goaway.c",
+    "src/core/transport/chttp2/frame_ping.c",
+    "src/core/transport/chttp2/frame_rst_stream.c",
+    "src/core/transport/chttp2/frame_settings.c",
+    "src/core/transport/chttp2/frame_window_update.c",
+    "src/core/transport/chttp2/hpack_encoder.c",
+    "src/core/transport/chttp2/hpack_parser.c",
+    "src/core/transport/chttp2/hpack_table.c",
+    "src/core/transport/chttp2/huffsyms.c",
+    "src/core/transport/chttp2/incoming_metadata.c",
+    "src/core/transport/chttp2/parsing.c",
+    "src/core/transport/chttp2/status_conversion.c",
+    "src/core/transport/chttp2/stream_lists.c",
+    "src/core/transport/chttp2/stream_map.c",
+    "src/core/transport/chttp2/timeout_encoding.c",
+    "src/core/transport/chttp2/varint.c",
+    "src/core/transport/chttp2/writing.c",
+    "src/core/transport/chttp2_transport.c",
+    "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",
+    "src/core/census/initialize.c",
+    "src/core/census/operation.c",
+    "src/core/census/tracing.c",
+    "include/grpc/support/alloc.h",
+    "include/grpc/support/atm.h",
+    "include/grpc/support/atm_gcc_atomic.h",
+    "include/grpc/support/atm_gcc_sync.h",
+    "include/grpc/support/atm_win32.h",
+    "include/grpc/support/avl.h",
+    "include/grpc/support/cmdline.h",
+    "include/grpc/support/cpu.h",
+    "include/grpc/support/histogram.h",
+    "include/grpc/support/host_port.h",
+    "include/grpc/support/log.h",
+    "include/grpc/support/log_win32.h",
+    "include/grpc/support/port_platform.h",
+    "include/grpc/support/slice.h",
+    "include/grpc/support/slice_buffer.h",
+    "include/grpc/support/string_util.h",
+    "include/grpc/support/subprocess.h",
+    "include/grpc/support/sync.h",
+    "include/grpc/support/sync_generic.h",
+    "include/grpc/support/sync_posix.h",
+    "include/grpc/support/sync_win32.h",
+    "include/grpc/support/thd.h",
+    "include/grpc/support/time.h",
+    "include/grpc/support/tls.h",
+    "include/grpc/support/tls_gcc.h",
+    "include/grpc/support/tls_msvc.h",
+    "include/grpc/support/tls_pthread.h",
+    "include/grpc/support/useful.h",
+    "src/core/profiling/timers.h",
+    "src/core/support/block_annotate.h",
+    "src/core/support/env.h",
+    "src/core/support/file.h",
+    "src/core/support/murmur_hash.h",
+    "src/core/support/stack_lockfree.h",
+    "src/core/support/string.h",
+    "src/core/support/string_win32.h",
+    "src/core/support/thd_internal.h",
+    "src/core/support/time_precise.h",
+    "src/core/profiling/basic_timers.c",
+    "src/core/profiling/stap_timers.c",
+    "src/core/support/alloc.c",
+    "src/core/support/avl.c",
+    "src/core/support/cmdline.c",
+    "src/core/support/cpu_iphone.c",
+    "src/core/support/cpu_linux.c",
+    "src/core/support/cpu_posix.c",
+    "src/core/support/cpu_windows.c",
+    "src/core/support/env_linux.c",
+    "src/core/support/env_posix.c",
+    "src/core/support/env_win32.c",
+    "src/core/support/file.c",
+    "src/core/support/file_posix.c",
+    "src/core/support/file_win32.c",
+    "src/core/support/histogram.c",
+    "src/core/support/host_port.c",
+    "src/core/support/log.c",
+    "src/core/support/log_android.c",
+    "src/core/support/log_linux.c",
+    "src/core/support/log_posix.c",
+    "src/core/support/log_win32.c",
+    "src/core/support/murmur_hash.c",
+    "src/core/support/slice.c",
+    "src/core/support/slice_buffer.c",
+    "src/core/support/stack_lockfree.c",
+    "src/core/support/string.c",
+    "src/core/support/string_posix.c",
+    "src/core/support/string_win32.c",
+    "src/core/support/subprocess_posix.c",
+    "src/core/support/sync.c",
+    "src/core/support/sync_posix.c",
+    "src/core/support/sync_win32.c",
+    "src/core/support/thd.c",
+    "src/core/support/thd_posix.c",
+    "src/core/support/thd_win32.c",
+    "src/core/support/time.c",
+    "src/core/support/time_posix.c",
+    "src/core/support/time_precise.c",
+    "src/core/support/time_win32.c",
+    "src/core/support/tls_pthread.c",
     "binding.gyp"
   ],
   "main": "src/node/index.js",
diff --git a/src/python/grpcio/requirements.txt b/requirements.txt
similarity index 62%
rename from src/python/grpcio/requirements.txt
rename to requirements.txt
index 06516ee..9d00274 100644
--- a/src/python/grpcio/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,4 @@
+# GRPC Python setup requirements
 enum34>=1.0.4
 futures>=2.2.0
 cython>=0.23
diff --git a/src/python/grpcio/setup.cfg b/setup.cfg
similarity index 79%
rename from src/python/grpcio/setup.cfg
rename to setup.cfg
index 52b6b50..add6ee8 100644
--- a/src/python/grpcio/setup.cfg
+++ b/setup.cfg
@@ -1,3 +1,5 @@
+# Setup settings for GRPC Python
+
 [coverage:run]
 plugins = Cython.Coverage
 
diff --git a/src/python/grpcio/setup.py b/setup.py
similarity index 72%
rename from src/python/grpcio/setup.py
rename to setup.py
index 366ebe3..b70fab3 100644
--- a/src/python/grpcio/setup.py
+++ b/setup.py
@@ -1,4 +1,4 @@
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -31,17 +31,30 @@
 
 import os
 import os.path
+import shutil
 import sys
 
 from distutils import core as _core
 from distutils import extension as _extension
 import setuptools
+from setuptools.command import egg_info
+
+# Redirect the manifest template from MANIFEST.in to PYTHON-MANIFEST.in.
+egg_info.manifest_maker.template = 'PYTHON-MANIFEST.in'
+
+PYTHON_STEM = './src/python/grpcio/'
+CORE_INCLUDE = ('./include', './',)
+BORINGSSL_INCLUDE = ('./third_party/boringssl/include',)
 
 # Ensure we're in the proper directory whether or not we're being used by pip.
 os.chdir(os.path.dirname(os.path.abspath(__file__)))
+sys.path.insert(0, PYTHON_STEM)
 
-# Break import-style to ensure we can actually find our commands module.
+# Break import-style to ensure we can actually find our in-repo dependencies.
 import commands
+import grpc_core_dependencies
+
+LICENSE = '3-clause BSD'
 
 # Environment variable to determine whether or not the Cython extension should
 # *use* Cython or use the generated C files. Note that this requires the C files
@@ -59,44 +72,38 @@
 
 CYTHON_EXTENSION_PACKAGE_NAMES = ()
 
-CYTHON_EXTENSION_MODULE_NAMES = (
-    'grpc._cython.cygrpc',
-    'grpc._cython._cygrpc.call',
-    'grpc._cython._cygrpc.channel',
-    'grpc._cython._cygrpc.completion_queue',
-    'grpc._cython._cygrpc.credentials',
-    'grpc._cython._cygrpc.records',
-    'grpc._cython._cygrpc.server',
-)
+CYTHON_EXTENSION_MODULE_NAMES = ('grpc._cython.cygrpc',)
 
 EXTENSION_INCLUDE_DIRECTORIES = (
-    '.',
-)
+    (PYTHON_STEM,) + CORE_INCLUDE + BORINGSSL_INCLUDE)
 
-EXTENSION_LIBRARIES = (
-    'grpc',
-    'gpr',
-)
+EXTENSION_LIBRARIES = ()
 if not "darwin" in sys.platform:
     EXTENSION_LIBRARIES += ('rt',)
 
+DEFINE_MACROS = (('OPENSSL_NO_ASM', 1),)
 
 def cython_extensions(package_names, module_names, include_dirs, libraries,
-                      build_with_cython=False):
+                      define_macros, build_with_cython=False):
+  if ENABLE_CYTHON_TRACING:
+    define_macros = define_macros + [('CYTHON_TRACE_NOGIL', 1)]
   file_extension = 'pyx' if build_with_cython else 'c'
-  module_files = [name.replace('.', '/') + '.' + file_extension
+  module_files = [os.path.join(PYTHON_STEM,
+                               name.replace('.', '/') + '.' + file_extension)
                   for name in module_names]
   extensions = [
       _extension.Extension(
-          name=module_name, sources=[module_file],
+          name=module_name,
+          sources=[module_file] + grpc_core_dependencies.CORE_SOURCE_FILES,
           include_dirs=include_dirs, libraries=libraries,
-          define_macros=[('CYTHON_TRACE_NOGIL', 1)] if ENABLE_CYTHON_TRACING else []
+          define_macros=define_macros,
       ) for (module_name, module_file) in zip(module_names, module_files)
   ]
   if build_with_cython:
     import Cython.Build
     return Cython.Build.cythonize(
         extensions,
+        include_path=include_dirs,
         compiler_directives={'linetrace': bool(ENABLE_CYTHON_TRACING)})
   else:
     return extensions
@@ -104,10 +111,10 @@
 CYTHON_EXTENSION_MODULES = cython_extensions(
     list(CYTHON_EXTENSION_PACKAGE_NAMES), list(CYTHON_EXTENSION_MODULE_NAMES),
     list(EXTENSION_INCLUDE_DIRECTORIES), list(EXTENSION_LIBRARIES),
-    bool(BUILD_WITH_CYTHON))
+    list(DEFINE_MACROS), bool(BUILD_WITH_CYTHON))
 
 PACKAGE_DIRECTORIES = {
-    '': '.',
+    '': PYTHON_STEM,
 }
 
 INSTALL_REQUIRES = (
@@ -124,10 +131,19 @@
     'build_proto_modules': commands.BuildProtoModules,
     'build_project_metadata': commands.BuildProjectMetadata,
     'build_py': commands.BuildPy,
+    'build_ext': commands.BuildExt,
     'gather': commands.Gather,
     'run_interop': commands.RunInterop,
 }
 
+# Ensure that package data is copied over before any commands have been run:
+credentials_dir = os.path.join(PYTHON_STEM, 'grpc/_adapter/credentials')
+try:
+  os.mkdir(credentials_dir)
+except OSError:
+  pass
+shutil.copyfile('etc/roots.pem', os.path.join(credentials_dir, 'roots.pem'))
+
 TEST_PACKAGE_DATA = {
     'tests.interop': [
         'credentials/ca.pem',
@@ -142,6 +158,9 @@
         'credentials/server1.key',
         'credentials/server1.pem',
     ],
+    'grpc._adapter': [
+        'credentials/roots.pem'
+    ],
 }
 
 TESTS_REQUIRE = (
@@ -157,16 +176,19 @@
 PACKAGE_DATA = {}
 if INSTALL_TESTS:
   PACKAGE_DATA = dict(PACKAGE_DATA, **TEST_PACKAGE_DATA)
-  PACKAGES = setuptools.find_packages('.')
+  PACKAGES = setuptools.find_packages(PYTHON_STEM)
 else:
-  PACKAGES = setuptools.find_packages('.', exclude=['tests', 'tests.*'])
+  PACKAGES = setuptools.find_packages(
+      PYTHON_STEM, exclude=['tests', 'tests.*'])
 
 setuptools.setup(
     name='grpcio',
-    version='0.12.0b0',
+    version='0.12.0b5',
+    license=LICENSE,
     ext_modules=CYTHON_EXTENSION_MODULES,
     packages=list(PACKAGES),
     package_dir=PACKAGE_DIRECTORIES,
+    package_data=PACKAGE_DATA,
     install_requires=INSTALL_REQUIRES,
     setup_requires=SETUP_REQUIRES,
     cmdclass=COMMAND_CLASS,
diff --git a/src/core/census/initialize.c b/src/core/census/initialize.c
index 8d60f79..989708d 100644
--- a/src/core/census/initialize.c
+++ b/src/core/census/initialize.c
@@ -39,12 +39,11 @@
   if (features_enabled != CENSUS_FEATURE_NONE) {
     return 1;
   }
-  if (features != CENSUS_FEATURE_NONE) {
+  if (features == CENSUS_FEATURE_NONE) {
     return 1;
-  } else {
-    features_enabled = features;
-    return 0;
   }
+  features_enabled = features;
+  return 0;
 }
 
 void census_shutdown(void) { features_enabled = CENSUS_FEATURE_NONE; }
diff --git a/src/core/client_config/lb_policies/pick_first.c b/src/core/client_config/lb_policies/pick_first.c
index 4a90b07..e6ddb1a 100644
--- a/src/core/client_config/lb_policies/pick_first.c
+++ b/src/core/client_config/lb_policies/pick_first.c
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -75,7 +75,8 @@
   grpc_connectivity_state_tracker state_tracker;
 } pick_first_lb_policy;
 
-#define GET_SELECTED(p) ((grpc_connected_subchannel *)gpr_atm_no_barrier_load(&(p)->selected))
+#define GET_SELECTED(p) \
+  ((grpc_connected_subchannel *)gpr_atm_no_barrier_load(&(p)->selected))
 
 void pf_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
   pick_first_lb_policy *p = (pick_first_lb_policy *)pol;
@@ -264,7 +265,8 @@
         grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
                                     GRPC_CHANNEL_READY, "connecting_ready");
         selected_subchannel = p->subchannels[p->checking_subchannel];
-        selected = grpc_subchannel_get_connected_subchannel(selected_subchannel);
+        selected =
+            grpc_subchannel_get_connected_subchannel(selected_subchannel);
         GPR_ASSERT(selected != NULL);
         gpr_atm_no_barrier_store(&p->selected, (gpr_atm)selected);
         GRPC_CONNECTED_SUBCHANNEL_REF(selected, "picked_first");
diff --git a/src/core/iomgr/fd_posix.c b/src/core/iomgr/fd_posix.c
index 079cb06..89c938b 100644
--- a/src/core/iomgr/fd_posix.c
+++ b/src/core/iomgr/fd_posix.c
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/src/core/iomgr/fd_posix.h b/src/core/iomgr/fd_posix.h
index 8062dd0..17e7de8 100644
--- a/src/core/iomgr/fd_posix.h
+++ b/src/core/iomgr/fd_posix.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/src/core/iomgr/pollset_multipoller_with_epoll.c b/src/core/iomgr/pollset_multipoller_with_epoll.c
index dae33e4..911d820 100644
--- a/src/core/iomgr/pollset_multipoller_with_epoll.c
+++ b/src/core/iomgr/pollset_multipoller_with_epoll.c
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/src/core/iomgr/pollset_multipoller_with_poll_posix.c b/src/core/iomgr/pollset_multipoller_with_poll_posix.c
index a7282b9..809f8f3 100644
--- a/src/core/iomgr/pollset_multipoller_with_poll_posix.c
+++ b/src/core/iomgr/pollset_multipoller_with_poll_posix.c
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/src/core/iomgr/pollset_posix.h b/src/core/iomgr/pollset_posix.h
index 8b1616a..b34bb09 100644
--- a/src/core/iomgr/pollset_posix.h
+++ b/src/core/iomgr/pollset_posix.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/src/core/iomgr/tcp_posix.c b/src/core/iomgr/tcp_posix.c
index 65783a7..4fa8ca8 100644
--- a/src/core/iomgr/tcp_posix.c
+++ b/src/core/iomgr/tcp_posix.c
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/src/core/iomgr/tcp_posix.h b/src/core/iomgr/tcp_posix.h
index 495ed00..2a40cdd 100644
--- a/src/core/iomgr/tcp_posix.h
+++ b/src/core/iomgr/tcp_posix.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/src/core/security/base64.c b/src/core/security/base64.c
index 0c36457..8367c16 100644
--- a/src/core/security/base64.c
+++ b/src/core/security/base64.c
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/src/core/support/sync_posix.c b/src/core/support/sync_posix.c
index 4d59a10..d3c483f 100644
--- a/src/core/support/sync_posix.c
+++ b/src/core/support/sync_posix.c
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/src/core/surface/call.c b/src/core/surface/call.c
index b1ba2af..880666b 100644
--- a/src/core/surface/call.c
+++ b/src/core/surface/call.c
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -37,6 +37,7 @@
 #include <string.h>
 
 #include <grpc/compression.h>
+#include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
@@ -562,12 +563,16 @@
     GPR_ASSERT(sizeof(grpc_linked_mdelem) == sizeof(md->internal_data));
     l->md = grpc_mdelem_from_string_and_buffer(
         md->key, (const uint8_t *)md->value, md->value_length);
-    if (!grpc_mdstr_is_legal_header(l->md->key)) {
+    if (!grpc_header_key_is_legal(grpc_mdstr_as_c_string(l->md->key),
+                                  GRPC_MDSTR_LENGTH(l->md->key))) {
       gpr_log(GPR_ERROR, "attempt to send invalid metadata key: %s",
               grpc_mdstr_as_c_string(l->md->key));
       return 0;
-    } else if (!grpc_mdstr_is_bin_suffixed(l->md->key) &&
-               !grpc_mdstr_is_legal_nonbin_header(l->md->value)) {
+    } else if (!grpc_is_binary_header(grpc_mdstr_as_c_string(l->md->key),
+                                      GRPC_MDSTR_LENGTH(l->md->key)) &&
+               !grpc_header_nonbin_value_is_legal(
+                   grpc_mdstr_as_c_string(l->md->value),
+                   GRPC_MDSTR_LENGTH(l->md->value))) {
       gpr_log(GPR_ERROR, "attempt to send invalid metadata value");
       return 0;
     }
diff --git a/src/core/surface/validate_metadata.c b/src/core/surface/validate_metadata.c
new file mode 100644
index 0000000..df2e80b
--- /dev/null
+++ b/src/core/surface/validate_metadata.c
@@ -0,0 +1,73 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/port_platform.h>
+
+static int conforms_to(const char *s, size_t len, const uint8_t *legal_bits) {
+  const char *p = s;
+  const char *e = s + len;
+  for (; p != e; p++) {
+    int idx = *p;
+    int byte = idx / 8;
+    int bit = idx % 8;
+    if ((legal_bits[byte] & (1 << bit)) == 0) return 0;
+  }
+  return 1;
+}
+
+int grpc_header_key_is_legal(const char *key, size_t length) {
+  static const uint8_t legal_header_bits[256 / 8] = {
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xff, 0x03, 0x00, 0x00, 0x00,
+      0x80, 0xfe, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+  if (length == 0) {
+    return 0;
+  }
+  return conforms_to(key, length, legal_header_bits);
+}
+
+int grpc_header_nonbin_value_is_legal(const char *value, size_t length) {
+  static const uint8_t legal_header_bits[256 / 8] = {
+      0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+      0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+  return conforms_to(value, length, legal_header_bits);
+}
+
+int grpc_is_binary_header(const char *key, size_t length) {
+  if (length < 5) return 0;
+  return 0 == memcmp(key + length - 4, "-bin", 4);
+}
diff --git a/src/core/transport/chttp2/bin_encoder.c b/src/core/transport/chttp2/bin_encoder.c
index a6a8e3e..f26bc7e 100644
--- a/src/core/transport/chttp2/bin_encoder.c
+++ b/src/core/transport/chttp2/bin_encoder.c
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -283,8 +283,3 @@
   GPR_ASSERT(in == GPR_SLICE_END_PTR(input));
   return output;
 }
-
-int grpc_is_binary_header(const char *key, size_t length) {
-  if (length < 5) return 0;
-  return 0 == memcmp(key + length - 4, "-bin", 4);
-}
diff --git a/src/core/transport/chttp2/bin_encoder.h b/src/core/transport/chttp2/bin_encoder.h
index d3e5a85..036fddf 100644
--- a/src/core/transport/chttp2/bin_encoder.h
+++ b/src/core/transport/chttp2/bin_encoder.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -51,6 +51,4 @@
    return y; */
 gpr_slice grpc_chttp2_base64_encode_and_huffman_compress(gpr_slice input);
 
-int grpc_is_binary_header(const char *key, size_t length);
-
 #endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_BIN_ENCODER_H */
diff --git a/src/core/transport/chttp2/hpack_encoder.c b/src/core/transport/chttp2/hpack_encoder.c
index d6a352a..89a80d8 100644
--- a/src/core/transport/chttp2/hpack_encoder.c
+++ b/src/core/transport/chttp2/hpack_encoder.c
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -36,6 +36,11 @@
 #include <assert.h>
 #include <string.h>
 
+/* This is here for grpc_is_binary_header
+ * TODO(murgatroid99): Remove this
+ */
+#include <grpc/grpc.h>
+
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/useful.h>
diff --git a/src/core/transport/chttp2/hpack_parser.c b/src/core/transport/chttp2/hpack_parser.c
index 16395d6..a63c7db 100644
--- a/src/core/transport/chttp2/hpack_parser.c
+++ b/src/core/transport/chttp2/hpack_parser.c
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -38,6 +38,11 @@
 #include <string.h>
 #include <assert.h>
 
+/* This is here for grpc_is_binary_header
+ * TODO(murgatroid99): Remove this
+ */
+#include <grpc/grpc.h>
+
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/port_platform.h>
diff --git a/src/core/transport/chttp2/internal.h b/src/core/transport/chttp2/internal.h
index 0b0fccf..a8262b7 100644
--- a/src/core/transport/chttp2/internal.h
+++ b/src/core/transport/chttp2/internal.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c
index c154c07..05b25fd 100644
--- a/src/core/transport/chttp2_transport.c
+++ b/src/core/transport/chttp2_transport.c
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/src/core/transport/metadata.c b/src/core/transport/metadata.c
index ada8899..14912af 100644
--- a/src/core/transport/metadata.c
+++ b/src/core/transport/metadata.c
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -688,37 +688,3 @@
   gpr_mu_unlock(&shard->mu);
   return slice;
 }
-
-static int conforms_to(grpc_mdstr *s, const uint8_t *legal_bits) {
-  const uint8_t *p = GPR_SLICE_START_PTR(s->slice);
-  const uint8_t *e = GPR_SLICE_END_PTR(s->slice);
-  for (; p != e; p++) {
-    int idx = *p;
-    int byte = idx / 8;
-    int bit = idx % 8;
-    if ((legal_bits[byte] & (1 << bit)) == 0) return 0;
-  }
-  return 1;
-}
-
-int grpc_mdstr_is_legal_header(grpc_mdstr *s) {
-  static const uint8_t legal_header_bits[256 / 8] = {
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xff, 0x03, 0x00, 0x00, 0x00,
-      0x80, 0xfe, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-  return conforms_to(s, legal_header_bits);
-}
-
-int grpc_mdstr_is_legal_nonbin_header(grpc_mdstr *s) {
-  static const uint8_t legal_header_bits[256 / 8] = {
-      0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-      0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-  return conforms_to(s, legal_header_bits);
-}
-
-int grpc_mdstr_is_bin_suffixed(grpc_mdstr *s) {
-  /* TODO(ctiller): consider caching this */
-  return grpc_is_binary_header((const char *)GPR_SLICE_START_PTR(s->slice),
-                               GPR_SLICE_LENGTH(s->slice));
-}
diff --git a/src/core/transport/metadata.h b/src/core/transport/metadata.h
index a7c9fd6..8742846 100644
--- a/src/core/transport/metadata.h
+++ b/src/core/transport/metadata.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -142,6 +142,8 @@
    Does not promise that the returned string has no embedded nulls however. */
 const char *grpc_mdstr_as_c_string(grpc_mdstr *s);
 
+#define GRPC_MDSTR_LENGTH(s) (GPR_SLICE_LENGTH(s->slice))
+
 int grpc_mdstr_is_legal_header(grpc_mdstr *s);
 int grpc_mdstr_is_legal_nonbin_header(grpc_mdstr *s);
 int grpc_mdstr_is_bin_suffixed(grpc_mdstr *s);
diff --git a/src/cpp/util/byte_buffer.cc b/src/cpp/util/byte_buffer.cc
index 755234d..2952f94 100644
--- a/src/cpp/util/byte_buffer.cc
+++ b/src/cpp/util/byte_buffer.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -69,6 +69,7 @@
   while (grpc_byte_buffer_reader_next(&reader, &s)) {
     slices->push_back(Slice(s, Slice::STEAL_REF));
   }
+  grpc_byte_buffer_reader_destroy(&reader);
 }
 
 size_t ByteBuffer::Length() const {
@@ -79,4 +80,13 @@
   }
 }
 
+ByteBuffer::ByteBuffer(const ByteBuffer& buf)
+    : buffer_(grpc_byte_buffer_copy(buf.buffer_)) {}
+
+ByteBuffer& ByteBuffer::operator=(const ByteBuffer& buf) {
+  Clear();                                       // first remove existing data
+  buffer_ = grpc_byte_buffer_copy(buf.buffer_);  // then copy
+  return *this;
+}
+
 }  // namespace grpc
diff --git a/src/csharp/generate_proto_csharp.sh b/src/csharp/generate_proto_csharp.sh
index 8e75fea..3aeda21 100755
--- a/src/csharp/generate_proto_csharp.sh
+++ b/src/csharp/generate_proto_csharp.sh
@@ -1,5 +1,5 @@
 #!/bin/sh
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
diff --git a/src/node/ext/call.cc b/src/node/ext/call.cc
index c6e10bc..da31288 100644
--- a/src/node/ext/call.cc
+++ b/src/node/ext/call.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -95,10 +95,6 @@
   return scope.Escape(err);
 }
 
-bool EndsWith(const char *str, const char *substr) {
-  return strcmp(str+strlen(str)-strlen(substr), substr) == 0;
-}
-
 bool CreateMetadataArray(Local<Object> metadata, grpc_metadata_array *array,
                          shared_ptr<Resources> resources) {
   HandleScope scope;
@@ -126,7 +122,7 @@
       grpc_metadata *current = &array->metadata[array->count];
       current->key = **utf8_key;
       // Only allow binary headers for "-bin" keys
-      if (EndsWith(current->key, "-bin")) {
+      if (grpc_is_binary_header(current->key, strlen(current->key))) {
         if (::node::Buffer::HasInstance(value)) {
           current->value = ::node::Buffer::Data(value);
           current->value_length = ::node::Buffer::Length(value);
@@ -180,7 +176,7 @@
     } else {
       array = Local<Array>::Cast(maybe_array.ToLocalChecked());
     }
-    if (EndsWith(elem->key, "-bin")) {
+    if (grpc_is_binary_header(elem->key, strlen(elem->key))) {
       Nan::Set(array, index_map[elem->key],
                MakeFastBuffer(
                    Nan::CopyBuffer(elem->value,
diff --git a/src/node/ext/call_credentials.cc b/src/node/ext/call_credentials.cc
index 8cbfb1e..91acb86 100644
--- a/src/node/ext/call_credentials.cc
+++ b/src/node/ext/call_credentials.cc
@@ -32,6 +32,8 @@
  */
 
 #include <node.h>
+#include <nan.h>
+#include <uv.h>
 
 #include "grpc/grpc.h"
 #include "grpc/grpc_security.h"
diff --git a/src/node/ext/node_grpc.cc b/src/node/ext/node_grpc.cc
index 5b5f3c1..a2b8e0d 100644
--- a/src/node/ext/node_grpc.cc
+++ b/src/node/ext/node_grpc.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -44,6 +44,7 @@
 #include "completion_queue_async_worker.h"
 #include "server_credentials.h"
 
+using v8::FunctionTemplate;
 using v8::Local;
 using v8::Value;
 using v8::Object;
@@ -230,6 +231,40 @@
   Nan::Set(write_flags, Nan::New("NO_COMPRESS").ToLocalChecked(), NO_COMPRESS);
 }
 
+NAN_METHOD(MetadataKeyIsLegal) {
+  if (!info[0]->IsString()) {
+    return Nan::ThrowTypeError(
+        "headerKeyIsLegal's argument must be a string");
+  }
+  Local<String> key = Nan::To<String>(info[0]).ToLocalChecked();
+  char *key_str = *Nan::Utf8String(key);
+  info.GetReturnValue().Set(static_cast<bool>(
+      grpc_header_key_is_legal(key_str, static_cast<size_t>(key->Length()))));
+}
+
+NAN_METHOD(MetadataNonbinValueIsLegal) {
+  if (!info[0]->IsString()) {
+    return Nan::ThrowTypeError(
+        "metadataNonbinValueIsLegal's argument must be a string");
+  }
+  Local<String> value = Nan::To<String>(info[0]).ToLocalChecked();
+  char *value_str = *Nan::Utf8String(value);
+  info.GetReturnValue().Set(static_cast<bool>(
+      grpc_header_nonbin_value_is_legal(
+          value_str, static_cast<size_t>(value->Length()))));
+}
+
+NAN_METHOD(MetadataKeyIsBinary) {
+  if (!info[0]->IsString()) {
+    return Nan::ThrowTypeError(
+        "metadataKeyIsLegal's argument must be a string");
+  }
+  Local<String> key = Nan::To<String>(info[0]).ToLocalChecked();
+  char *key_str = *Nan::Utf8String(key);
+  info.GetReturnValue().Set(static_cast<bool>(
+      grpc_is_binary_header(key_str, static_cast<size_t>(key->Length()))));
+}
+
 void init(Local<Object> exports) {
   Nan::HandleScope scope;
   grpc_init();
@@ -247,6 +282,19 @@
   grpc::node::Server::Init(exports);
   grpc::node::CompletionQueueAsyncWorker::Init(exports);
   grpc::node::ServerCredentials::Init(exports);
+
+  // Attach a few utility functions directly to the module
+  Nan::Set(exports, Nan::New("metadataKeyIsLegal").ToLocalChecked(),
+           Nan::GetFunction(
+               Nan::New<FunctionTemplate>(MetadataKeyIsLegal)).ToLocalChecked());
+  Nan::Set(exports, Nan::New("metadataNonbinValueIsLegal").ToLocalChecked(),
+           Nan::GetFunction(
+               Nan::New<FunctionTemplate>(MetadataNonbinValueIsLegal)
+                            ).ToLocalChecked());
+  Nan::Set(exports, Nan::New("metadataKeyIsBinary").ToLocalChecked(),
+           Nan::GetFunction(
+               Nan::New<FunctionTemplate>(MetadataKeyIsBinary)
+                            ).ToLocalChecked());
 }
 
 NODE_MODULE(grpc_node, init)
diff --git a/src/node/ext/timeval.cc b/src/node/ext/timeval.cc
index bf68513..64015e8 100644
--- a/src/node/ext/timeval.cc
+++ b/src/node/ext/timeval.cc
@@ -46,7 +46,7 @@
   } else if (millis == -std::numeric_limits<double>::infinity()) {
     return gpr_inf_past(GPR_CLOCK_REALTIME);
   } else {
-    return gpr_time_from_micros(static_cast<int64_t>(millis * 1000),
+    return gpr_time_from_micros(static_cast<long>(millis * 1000),
                                 GPR_CLOCK_REALTIME);
   }
 }
diff --git a/src/node/interop/async_delay_queue.js b/src/node/interop/async_delay_queue.js
index 2bd3ca4..df57209 100644
--- a/src/node/interop/async_delay_queue.js
+++ b/src/node/interop/async_delay_queue.js
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -36,8 +36,8 @@
 var _ = require('lodash');
 
 /**
- * This class represents a queue of callbacks that must happen sequentially, each
- * with a specific delay after the previous event.
+ * This class represents a queue of callbacks that must happen sequentially,
+ * each with a specific delay after the previous event.
  */
 function AsyncDelayQueue() {
   this.queue = [];
diff --git a/src/node/interop/interop_client.js b/src/node/interop/interop_client.js
index 7e65d20..db383e4 100644
--- a/src/node/interop/interop_client.js
+++ b/src/node/interop/interop_client.js
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/src/node/interop/interop_server.js b/src/node/interop/interop_server.js
index 7280762..c094817 100644
--- a/src/node/interop/interop_server.js
+++ b/src/node/interop/interop_server.js
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/src/node/jsdoc_conf.json b/src/node/jsdoc_conf.json
index 876a8e1..c3a0174 100644
--- a/src/node/jsdoc_conf.json
+++ b/src/node/jsdoc_conf.json
@@ -3,13 +3,13 @@
     "allowUnknownTags": true
   },
   "source": {
-    "include": [ "index.js", "src" ],
-    "includePattern": ".+\\.js(doc)?$",
+    "include": [ "src/node/index.js", "src/node/src" ],
+    "includePattern": "src/node/.+\\.js(doc)?$",
     "excludePattern": "(^|\\/|\\\\)_"
   },
   "opts": {
     "package": "package.json",
-    "readme": "README.md"
+    "readme": "src/node/README.md"
   },
   "plugins": [],
   "templates": {
diff --git a/src/node/performance/benchmark_client.js b/src/node/performance/benchmark_client.js
index 9e956d4..620aecd 100644
--- a/src/node/performance/benchmark_client.js
+++ b/src/node/performance/benchmark_client.js
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/src/node/performance/benchmark_server.js b/src/node/performance/benchmark_server.js
index 858f219..ba61e52 100644
--- a/src/node/performance/benchmark_server.js
+++ b/src/node/performance/benchmark_server.js
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/src/node/performance/worker_server.js b/src/node/performance/worker_server.js
index 98577bd..7c8ab00 100644
--- a/src/node/performance/worker_server.js
+++ b/src/node/performance/worker_server.js
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/src/node/src/common.js b/src/node/src/common.js
index e4fe5a8..2e6c01c 100644
--- a/src/node/src/common.js
+++ b/src/node/src/common.js
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -125,7 +125,7 @@
   var prefix = '/' + fullyQualifiedName(service) + '/';
   return _.object(_.map(service.children, function(method) {
     return [_.camelCase(method.name), {
-      path: prefix + _.capitalize(method.name),
+      path: prefix + method.name,
       requestStream: method.requestStream,
       responseStream: method.responseStream,
       requestSerialize: serializeCls(method.resolvedRequestType.build()),
diff --git a/src/node/src/metadata.js b/src/node/src/metadata.js
index 0a2f148..fef79f9 100644
--- a/src/node/src/metadata.js
+++ b/src/node/src/metadata.js
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -49,6 +49,8 @@
 
 var _ = require('lodash');
 
+var grpc = require('bindings')('grpc_node');
+
 /**
  * Class for storing metadata. Keys are normalized to lowercase ASCII.
  * @constructor
@@ -58,15 +60,16 @@
 }
 
 function normalizeKey(key) {
-  if (!(/^[A-Za-z\d_-]+$/.test(key))) {
-    throw new Error('Metadata keys must be nonempty strings containing only ' +
-        'alphanumeric characters and hyphens');
+  key = key.toLowerCase();
+  if (grpc.metadataKeyIsLegal(key)) {
+    return key;
+  } else {
+    throw new Error('Metadata key contains illegal characters');
   }
-  return key.toLowerCase();
 }
 
 function validate(key, value) {
-  if (_.endsWith(key, '-bin')) {
+  if (grpc.metadataKeyIsBinary(key)) {
     if (!(value instanceof Buffer)) {
       throw new Error('keys that end with \'-bin\' must have Buffer values');
     }
@@ -75,9 +78,8 @@
       throw new Error(
           'keys that don\'t end with \'-bin\' must have String values');
     }
-    if (!(/^[\x20-\x7E]*$/.test(value))) {
-      throw new Error('Metadata string values can only contain printable ' +
-          'ASCII characters and space');
+    if (!grpc.metadataNonbinValueIsLegal(value)) {
+      throw new Error('Metadata string value contains illegal characters');
     }
   }
 }
diff --git a/src/objective-c/GRPCClient/GRPCCall.h b/src/objective-c/GRPCClient/GRPCCall.h
index c9fda42..7a77ae6 100644
--- a/src/objective-c/GRPCClient/GRPCCall.h
+++ b/src/objective-c/GRPCClient/GRPCCall.h
@@ -241,11 +241,11 @@
 @protocol GRPCRequestHeaders <NSObject>
 @property(nonatomic, readonly) NSUInteger count;
 
-- (id)objectForKeyedSubscript:(NSString *)key;
-- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key;
+- (id)objectForKeyedSubscript:(id)key;
+- (void)setObject:(id)obj forKeyedSubscript:(id)key;
 
 - (void)removeAllObjects;
-- (void)removeObjectForKey:(NSString *)key;
+- (void)removeObjectForKey:(id)key;
 @end
 
 #pragma clang diagnostic push
diff --git a/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj b/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj
index cfccdd4..2f57160 100644
--- a/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj
+++ b/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj
@@ -107,6 +107,7 @@
 				633BFFBF1B950B210007E424 /* Frameworks */,
 				633BFFC01B950B210007E424 /* Resources */,
 				AC2F6F9AB1C090BB0BEE6E4D /* Copy Pods Resources */,
+				A1738A987353B0BF2C64F0F7 /* Embed Pods Frameworks */,
 			);
 			buildRules = (
 			);
@@ -123,6 +124,7 @@
 		633BFFBA1B950B210007E424 /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
+				LastSwiftUpdateCheck = 0710;
 				LastUpgradeCheck = 0640;
 				ORGANIZATIONNAME = gRPC;
 				TargetAttributes = {
@@ -177,6 +179,21 @@
 			shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n    cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n    exit 1\nfi\n";
 			showEnvVarsInLog = 0;
 		};
+		A1738A987353B0BF2C64F0F7 /* Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Embed Pods Frameworks";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
 		AC2F6F9AB1C090BB0BEE6E4D /* Copy Pods Resources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
@@ -310,7 +327,7 @@
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_OBJC_BRIDGING_HEADER = "Bridging-Header.h";
-				USER_HEADER_SEARCH_PATHS = "Pods/**";
+				USER_HEADER_SEARCH_PATHS = "";
 			};
 			name = Debug;
 		};
@@ -323,7 +340,7 @@
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_OBJC_BRIDGING_HEADER = "Bridging-Header.h";
-				USER_HEADER_SEARCH_PATHS = "Pods/**";
+				USER_HEADER_SEARCH_PATHS = "";
 			};
 			name = Release;
 		};
diff --git a/src/proto/gen_build_yaml.py b/src/proto/gen_build_yaml.py
index 4e95983..e243d0d 100755
--- a/src/proto/gen_build_yaml.py
+++ b/src/proto/gen_build_yaml.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python2.7
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -36,21 +36,36 @@
 import re
 import sys
 
+def update_deps(key, proto_filename, deps, is_trans, visited):
+  if not proto_filename in visited:
+    visited.append(proto_filename)
+    with open(proto_filename) as inp:
+      for line in inp:
+        imp = re.search(r'import "([^"]*)"', line)
+        if not imp: continue
+        imp_proto = imp.group(1)
+        if key not in deps: deps[key] = []
+        deps[key].append(imp_proto[:-6])
+        if is_trans:
+          update_deps(key, imp_proto, deps, is_trans, visited)
+
 def main():
+  proto_dir = os.path.abspath(os.path.dirname(sys.argv[0]))
+  os.chdir(os.path.join(proto_dir, '../..'))
+
   deps = {}
-  for root, dirs, files in os.walk(os.path.dirname(sys.argv[0])):
+  deps_trans = {}
+  for root, dirs, files in os.walk('src/proto'):
     for f in files:
       if f[-6:] != '.proto': continue
       look_at = os.path.join(root, f)
-      with open(look_at) as inp:
-        for line in inp:
-          imp = re.search(r'import "([^"]*)"', line)
-          if not imp: continue
-          if look_at[:-6] not in deps: deps[look_at[:-6]] = []
-          deps[look_at[:-6]].append(imp.group(1)[:-6])
+      deps_for = look_at[:-6]
+      update_deps(deps_for, look_at, deps, False, [])      # First level deps
+      update_deps(deps_for, look_at, deps_trans, True, []) # Transitive deps
 
   json = {
-    'proto_deps': deps
+    'proto_deps': deps,
+    'proto_transitive_deps': deps_trans
   }
 
   print yaml.dump(json)
diff --git a/src/proto/grpc/testing/duplicate/echo_duplicate.proto b/src/proto/grpc/testing/duplicate/echo_duplicate.proto
index d5891db..9d84de1 100644
--- a/src/proto/grpc/testing/duplicate/echo_duplicate.proto
+++ b/src/proto/grpc/testing/duplicate/echo_duplicate.proto
@@ -1,5 +1,5 @@
 
-// Copyright 2015, Google Inc.
+// Copyright 2015-2016, Google Inc.
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without
@@ -36,6 +36,6 @@
 
 package grpc.testing.duplicate;
 
-service TestService {
+service EchoTestService {
   rpc Echo(grpc.testing.EchoRequest) returns (grpc.testing.EchoResponse);
 }
diff --git a/src/proto/grpc/testing/echo.proto b/src/proto/grpc/testing/echo.proto
index 0896f50..06c3baf 100644
--- a/src/proto/grpc/testing/echo.proto
+++ b/src/proto/grpc/testing/echo.proto
@@ -1,5 +1,5 @@
 
-// Copyright 2015, Google Inc.
+// Copyright 2015-2016, Google Inc.
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without
@@ -34,7 +34,7 @@
 
 package grpc.testing;
 
-service TestService {
+service EchoTestService {
   rpc Echo(EchoRequest) returns (EchoResponse);
   rpc RequestStream(stream EchoRequest) returns (EchoResponse);
   rpc ResponseStream(EchoRequest) returns (stream EchoResponse);
diff --git a/src/python/grpcio/.gitignore b/src/python/grpcio/.gitignore
index 95b96f7..1d804e1 100644
--- a/src/python/grpcio/.gitignore
+++ b/src/python/grpcio/.gitignore
@@ -14,3 +14,4 @@
 doc/
 _grpcio_metadata.py
 htmlcov/
+grpc/_adapter/credentials
diff --git a/src/python/grpcio/MANIFEST.in b/src/python/grpcio/MANIFEST.in
deleted file mode 100644
index 407eeab..0000000
--- a/src/python/grpcio/MANIFEST.in
+++ /dev/null
@@ -1,4 +0,0 @@
-graft grpc
-graft tests
-include commands.py
-include requirements.txt
diff --git a/src/python/grpcio/commands.py b/src/python/grpcio/commands.py
index d9fd023..e1a3f4b 100644
--- a/src/python/grpcio/commands.py
+++ b/src/python/grpcio/commands.py
@@ -1,4 +1,4 @@
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -40,6 +40,19 @@
 from setuptools.command import build_py
 from setuptools.command import test
 
+# Because we need to support building without Cython but simultaneously need to
+# subclass its command class when we need to and because distutils requires a
+# special hook to acquire a command class, we attempt to import Cython's
+# build_ext, and if that fails we import setuptools'.
+try:
+  # Due to the strange way Cython's Distutils module re-imports build_ext, we
+  # import the build_ext class directly.
+  from Cython.Distutils.build_ext import build_ext
+except ImportError:
+  from setuptools.command.build_ext import build_ext
+
+PYTHON_STEM = os.path.dirname(os.path.abspath(__file__))
+
 CONF_PY_ADDENDUM = """
 extensions.append('sphinx.ext.napoleon')
 napoleon_google_docstring = True
@@ -49,6 +62,10 @@
 """
 
 
+class CommandError(Exception):
+  """Simple exception class for GRPC custom commands."""
+
+
 class SphinxDocumentation(setuptools.Command):
   """Command to generate documentation via sphinx."""
 
@@ -68,7 +85,7 @@
     import sphinx.apidoc
     metadata = self.distribution.metadata
     src_dir = os.path.join(
-        os.getcwd(), self.distribution.package_dir[''], 'grpc')
+        PYTHON_STEM, self.distribution.package_dir[''], 'grpc')
     sys.path.append(src_dir)
     sphinx.apidoc.main([
         '', '--force', '--full', '-H', metadata.name, '-A', metadata.author,
@@ -101,10 +118,15 @@
         'grpc_python_plugin')
 
   def run(self):
+    if not self.protoc_command:
+      raise CommandError('could not find protoc')
+    if not self.grpc_python_plugin_command:
+      raise CommandError('could not find grpc_python_plugin '
+                         '(protoc plugin for GRPC Python)')
     include_regex = re.compile(self.include)
     exclude_regex = re.compile(self.exclude) if self.exclude else None
     paths = []
-    root_directory = os.getcwd()
+    root_directory = PYTHON_STEM
     for walk_root, directories, filenames in os.walk(root_directory):
       for filename in filenames:
         path = os.path.join(walk_root, filename)
@@ -123,7 +145,7 @@
       subprocess.check_output(' '.join(command), cwd=root_directory, shell=True,
                               stderr=subprocess.STDOUT)
     except subprocess.CalledProcessError as e:
-      raise Exception('Command:\n{}\nMessage:\n{}\nOutput:\n{}'.format(
+      raise CommandError('Command:\n{}\nMessage:\n{}\nOutput:\n{}'.format(
           command, e.message, e.output))
 
 
@@ -140,7 +162,7 @@
     pass
 
   def run(self):
-    with open('grpc/_grpcio_metadata.py', 'w') as module_file:
+    with open(os.path.join(PYTHON_STEM, 'grpc/_grpcio_metadata.py'), 'w') as module_file:
       module_file.write('__version__ = """{}"""'.format(
           self.distribution.get_version()))
 
@@ -149,11 +171,34 @@
   """Custom project build command."""
 
   def run(self):
-    self.run_command('build_proto_modules')
+    try:
+      self.run_command('build_proto_modules')
+    except CommandError as error:
+      sys.stderr.write('warning: %s\n' % error.message)
     self.run_command('build_project_metadata')
     build_py.build_py.run(self)
 
 
+class BuildExt(build_ext):
+  """Custom build_ext command to enable compiler-specific flags."""
+
+  C_OPTIONS = {
+      'unix': ('-pthread', '-std=gnu99'),
+      'msvc': (),
+  }
+  LINK_OPTIONS = {}
+
+  def build_extensions(self):
+    compiler = self.compiler.compiler_type
+    if compiler in BuildExt.C_OPTIONS:
+      for extension in self.extensions:
+        extension.extra_compile_args += list(BuildExt.C_OPTIONS[compiler])
+    if compiler in BuildExt.LINK_OPTIONS:
+      for extension in self.extensions:
+        extension.extra_link_args += list(BuildExt.LINK_OPTIONS[compiler])
+    build_ext.build_extensions(self)
+
+
 class Gather(setuptools.Command):
   """Command to gather project dependencies."""
 
diff --git a/src/python/grpcio/grpc/_adapter/_low.py b/src/python/grpcio/grpc/_adapter/_low.py
index b13d8dd..a850c57 100644
--- a/src/python/grpcio/grpc/_adapter/_low.py
+++ b/src/python/grpcio/grpc/_adapter/_low.py
@@ -1,4 +1,4 @@
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -27,6 +27,7 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+import pkg_resources
 import threading
 
 from grpc import _grpcio_metadata
@@ -34,6 +35,7 @@
 from grpc._adapter import _implementations
 from grpc._adapter import _types
 
+_ROOT_CERTIFICATES_RESOURCE_PATH = 'credentials/roots.pem'
 _USER_AGENT = 'Python-gRPC-{}'.format(_grpcio_metadata.__version__)
 
 ChannelCredentials = cygrpc.ChannelCredentials
@@ -54,6 +56,9 @@
   pair = None
   if private_key is not None or certificate_chain is not None:
     pair = cygrpc.SslPemKeyCertPair(private_key, certificate_chain)
+  if root_certificates is None:
+    root_certificates = pkg_resources.resource_string(
+      __name__, _ROOT_CERTIFICATES_RESOURCE_PATH)
   return cygrpc.channel_credentials_ssl(root_certificates, pair)
 
 
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/call.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/call.pxd.pxi
similarity index 95%
rename from src/python/grpcio/grpc/_cython/_cygrpc/call.pxd
rename to src/python/grpcio/grpc/_cython/_cygrpc/call.pxd.pxi
index fe9b81e..6a31674 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/call.pxd
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/call.pxd.pxi
@@ -27,11 +27,9 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-from grpc._cython._cygrpc cimport grpc
-
 
 cdef class Call:
 
-  cdef grpc.grpc_call *c_call
+  cdef grpc_call *c_call
   cdef list references
 
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx.pxi
similarity index 77%
rename from src/python/grpcio/grpc/_cython/_cygrpc/call.pyx
rename to src/python/grpcio/grpc/_cython/_cygrpc/call.pyx.pxi
index 1c07f9f..80f4da5 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx.pxi
@@ -29,10 +29,6 @@
 
 cimport cpython
 
-from grpc._cython._cygrpc cimport credentials
-from grpc._cython._cygrpc cimport grpc
-from grpc._cython._cygrpc cimport records
-
 
 cdef class Call:
 
@@ -44,24 +40,24 @@
   def start_batch(self, operations, tag):
     if not self.is_valid:
       raise ValueError("invalid call object cannot be used from Python")
-    cdef records.Operations cy_operations = records.Operations(operations)
-    cdef records.OperationTag operation_tag = records.OperationTag(tag)
+    cdef Operations cy_operations = Operations(operations)
+    cdef OperationTag operation_tag = OperationTag(tag)
     operation_tag.operation_call = self
     operation_tag.batch_operations = cy_operations
     cpython.Py_INCREF(operation_tag)
-    return grpc.grpc_call_start_batch(
+    return grpc_call_start_batch(
         self.c_call, cy_operations.c_ops, cy_operations.c_nops,
         <cpython.PyObject *>operation_tag, NULL)
 
   def cancel(
-      self, grpc.grpc_status_code error_code=grpc.GRPC_STATUS__DO_NOT_USE,
+      self, grpc_status_code error_code=GRPC_STATUS__DO_NOT_USE,
       details=None):
     if not self.is_valid:
       raise ValueError("invalid call object cannot be used from Python")
-    if (details is None) != (error_code == grpc.GRPC_STATUS__DO_NOT_USE):
+    if (details is None) != (error_code == GRPC_STATUS__DO_NOT_USE):
       raise ValueError("if error_code is specified, so must details "
                        "(and vice-versa)")
-    if error_code != grpc.GRPC_STATUS__DO_NOT_USE:
+    if error_code != GRPC_STATUS__DO_NOT_USE:
       if isinstance(details, bytes):
         pass
       elif isinstance(details, basestring):
@@ -69,25 +65,25 @@
       else:
         raise TypeError("expected details to be str or bytes")
       self.references.append(details)
-      return grpc.grpc_call_cancel_with_status(
+      return grpc_call_cancel_with_status(
           self.c_call, error_code, details, NULL)
     else:
-      return grpc.grpc_call_cancel(self.c_call, NULL)
+      return grpc_call_cancel(self.c_call, NULL)
 
   def set_credentials(
-      self, credentials.CallCredentials call_credentials not None):
-    return grpc.grpc_call_set_credentials(
+      self, CallCredentials call_credentials not None):
+    return grpc_call_set_credentials(
         self.c_call, call_credentials.c_credentials)
 
   def peer(self):
-    cdef char *peer = grpc.grpc_call_get_peer(self.c_call)
+    cdef char *peer = grpc_call_get_peer(self.c_call)
     result = <bytes>peer
-    grpc.gpr_free(peer)
+    gpr_free(peer)
     return result
 
   def __dealloc__(self):
     if self.c_call != NULL:
-      grpc.grpc_call_destroy(self.c_call)
+      grpc_call_destroy(self.c_call)
 
   # The object *should* always be valid from Python. Used for debugging.
   @property
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd.pxi
similarity index 94%
rename from src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd
rename to src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd.pxi
index 3e341bf..70da63d 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd.pxi
@@ -27,10 +27,8 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-from grpc._cython._cygrpc cimport grpc
-
 
 cdef class Channel:
 
-  cdef grpc.grpc_channel *c_channel
+  cdef grpc_channel *c_channel
   cdef list references
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi
similarity index 71%
rename from src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx
rename to src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi
index a944a83..ac67f32 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi
@@ -29,18 +29,12 @@
 
 cimport cpython
 
-from grpc._cython._cygrpc cimport call
-from grpc._cython._cygrpc cimport completion_queue
-from grpc._cython._cygrpc cimport credentials
-from grpc._cython._cygrpc cimport grpc
-from grpc._cython._cygrpc cimport records
-
 
 cdef class Channel:
 
-  def __cinit__(self, target, records.ChannelArgs arguments=None,
-                credentials.ChannelCredentials channel_credentials=None):
-    cdef grpc.grpc_channel_args *c_arguments = NULL
+  def __cinit__(self, target, ChannelArgs arguments=None,
+                ChannelCredentials channel_credentials=None):
+    cdef grpc_channel_args *c_arguments = NULL
     self.c_channel = NULL
     self.references = []
     if arguments is not None:
@@ -52,18 +46,18 @@
     else:
       raise TypeError("expected target to be str or bytes")
     if channel_credentials is None:
-      self.c_channel = grpc.grpc_insecure_channel_create(target, c_arguments,
+      self.c_channel = grpc_insecure_channel_create(target, c_arguments,
                                                          NULL)
     else:
-      self.c_channel = grpc.grpc_secure_channel_create(
+      self.c_channel = grpc_secure_channel_create(
           channel_credentials.c_credentials, target, c_arguments, NULL)
       self.references.append(channel_credentials)
     self.references.append(target)
     self.references.append(arguments)
 
-  def create_call(self, call.Call parent, int flags,
-                  completion_queue.CompletionQueue queue not None,
-                  method, host, records.Timespec deadline not None):
+  def create_call(self, Call parent, int flags,
+                  CompletionQueue queue not None,
+                  method, host, Timespec deadline not None):
     if queue.is_shutting_down:
       raise ValueError("queue must not be shutting down or shutdown")
     if isinstance(method, bytes):
@@ -82,36 +76,36 @@
       host_c_string = host
     else:
       raise TypeError("expected host to be str, bytes, or None")
-    cdef call.Call operation_call = call.Call()
+    cdef Call operation_call = Call()
     operation_call.references = [self, method, host, queue]
-    cdef grpc.grpc_call *parent_call = NULL
+    cdef grpc_call *parent_call = NULL
     if parent is not None:
       parent_call = parent.c_call
-    operation_call.c_call = grpc.grpc_channel_create_call(
+    operation_call.c_call = grpc_channel_create_call(
         self.c_channel, parent_call, flags,
         queue.c_completion_queue, method, host_c_string, deadline.c_time,
         NULL)
     return operation_call
 
   def check_connectivity_state(self, bint try_to_connect):
-    return grpc.grpc_channel_check_connectivity_state(self.c_channel,
+    return grpc_channel_check_connectivity_state(self.c_channel,
                                                       try_to_connect)
 
   def watch_connectivity_state(
-      self, last_observed_state, records.Timespec deadline not None,
-      completion_queue.CompletionQueue queue not None, tag):
-    cdef records.OperationTag operation_tag = records.OperationTag(tag)
+      self, last_observed_state, Timespec deadline not None,
+      CompletionQueue queue not None, tag):
+    cdef OperationTag operation_tag = OperationTag(tag)
     cpython.Py_INCREF(operation_tag)
-    grpc.grpc_channel_watch_connectivity_state(
+    grpc_channel_watch_connectivity_state(
         self.c_channel, last_observed_state, deadline.c_time,
         queue.c_completion_queue, <cpython.PyObject *>operation_tag)
 
   def target(self):
-    cdef char * target = grpc.grpc_channel_get_target(self.c_channel)
+    cdef char * target = grpc_channel_get_target(self.c_channel)
     result = <bytes>target
-    grpc.gpr_free(target)
+    gpr_free(target)
     return result
 
   def __dealloc__(self):
     if self.c_channel != NULL:
-      grpc.grpc_channel_destroy(self.c_channel)
+      grpc_channel_destroy(self.c_channel)
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pxd.pxi
similarity index 91%
rename from src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pxd
rename to src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pxd.pxi
index 1ed5d4b..757f124 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pxd
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pxd.pxi
@@ -27,15 +27,13 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-from grpc._cython._cygrpc cimport grpc
-
 
 cdef class CompletionQueue:
 
-  cdef grpc.grpc_completion_queue *c_completion_queue
+  cdef grpc_completion_queue *c_completion_queue
   cdef object poll_condition
   cdef bint is_polling
   cdef bint is_shutting_down
   cdef bint is_shutdown
 
-  cdef _interpret_event(self, grpc.grpc_event event)
+  cdef _interpret_event(self, grpc_event event)
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx.pxi
similarity index 76%
rename from src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx
rename to src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx.pxi
index 635a38f..bbf8413 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx.pxi
@@ -29,10 +29,6 @@
 
 cimport cpython
 
-from grpc._cython._cygrpc cimport call
-from grpc._cython._cygrpc cimport grpc
-from grpc._cython._cygrpc cimport records
-
 import threading
 import time
 
@@ -40,29 +36,29 @@
 cdef class CompletionQueue:
 
   def __cinit__(self):
-    self.c_completion_queue = grpc.grpc_completion_queue_create(NULL)
+    self.c_completion_queue = grpc_completion_queue_create(NULL)
     self.is_shutting_down = False
     self.is_shutdown = False
     self.poll_condition = threading.Condition()
     self.is_polling = False
 
-  cdef _interpret_event(self, grpc.grpc_event event):
-    cdef records.OperationTag tag = None
+  cdef _interpret_event(self, grpc_event event):
+    cdef OperationTag tag = None
     cdef object user_tag = None
-    cdef call.Call operation_call = None
-    cdef records.CallDetails request_call_details = None
-    cdef records.Metadata request_metadata = None
-    cdef records.Operations batch_operations = None
-    if event.type == grpc.GRPC_QUEUE_TIMEOUT:
-      return records.Event(
+    cdef Call operation_call = None
+    cdef CallDetails request_call_details = None
+    cdef Metadata request_metadata = None
+    cdef Operations batch_operations = None
+    if event.type == GRPC_QUEUE_TIMEOUT:
+      return Event(
           event.type, False, None, None, None, None, False, None)
-    elif event.type == grpc.GRPC_QUEUE_SHUTDOWN:
+    elif event.type == GRPC_QUEUE_SHUTDOWN:
       self.is_shutdown = True
-      return records.Event(
+      return Event(
           event.type, True, None, None, None, None, False, None)
     else:
       if event.tag != NULL:
-        tag = <records.OperationTag>event.tag
+        tag = <OperationTag>event.tag
         # We receive event tags only after they've been inc-ref'd elsewhere in
         # the code.
         cpython.Py_DECREF(tag)
@@ -77,19 +73,19 @@
           # Stuff in the tag not explicitly handled by us needs to live through
           # the life of the call
           operation_call.references.extend(tag.references)
-      return records.Event(
+      return Event(
           event.type, event.success, user_tag, operation_call,
           request_call_details, request_metadata, tag.is_new_request,
           batch_operations)
 
-  def poll(self, records.Timespec deadline=None):
+  def poll(self, Timespec deadline=None):
     # We name this 'poll' to avoid problems with CPython's expectations for
     # 'special' methods (like next and __next__).
-    cdef grpc.gpr_timespec c_deadline = grpc.gpr_inf_future(
-        grpc.GPR_CLOCK_REALTIME)
+    cdef gpr_timespec c_deadline = gpr_inf_future(
+        GPR_CLOCK_REALTIME)
     if deadline is not None:
       c_deadline = deadline.c_time
-    cdef grpc.grpc_event event
+    cdef grpc_event event
 
     # Poll within a critical section
     # TODO(atash) consider making queue polling contention a hard error to
@@ -99,21 +95,21 @@
         self.poll_condition.wait(float(deadline) - time.time())
       self.is_polling = True
     with nogil:
-      event = grpc.grpc_completion_queue_next(
+      event = grpc_completion_queue_next(
           self.c_completion_queue, c_deadline, NULL)
     with self.poll_condition:
       self.is_polling = False
       self.poll_condition.notify()
     return self._interpret_event(event)
 
-  def pluck(self, records.OperationTag tag, records.Timespec deadline=None):
+  def pluck(self, OperationTag tag, Timespec deadline=None):
     # Plucking a 'None' tag is equivalent to passing control to GRPC core until
     # the deadline.
-    cdef grpc.gpr_timespec c_deadline = grpc.gpr_inf_future(
-        grpc.GPR_CLOCK_REALTIME)
+    cdef gpr_timespec c_deadline = gpr_inf_future(
+        GPR_CLOCK_REALTIME)
     if deadline is not None:
       c_deadline = deadline.c_time
-    cdef grpc.grpc_event event
+    cdef grpc_event event
 
     # Poll within a critical section
     # TODO(atash) consider making queue polling contention a hard error to
@@ -123,7 +119,7 @@
         self.poll_condition.wait(float(deadline) - time.time())
       self.is_polling = True
     with nogil:
-      event = grpc.grpc_completion_queue_pluck(
+      event = grpc_completion_queue_pluck(
           self.c_completion_queue, <cpython.PyObject *>tag, c_deadline, NULL)
     with self.poll_condition:
       self.is_polling = False
@@ -131,13 +127,13 @@
     return self._interpret_event(event)
 
   def shutdown(self):
-    grpc.grpc_completion_queue_shutdown(self.c_completion_queue)
+    grpc_completion_queue_shutdown(self.c_completion_queue)
     self.is_shutting_down = True
 
   def clear(self):
     if not self.is_shutting_down:
       raise ValueError('queue must be shutting down to be cleared')
-    while self.poll().type != grpc.GRPC_QUEUE_SHUTDOWN:
+    while self.poll().type != GRPC_QUEUE_SHUTDOWN:
       pass
 
   def __dealloc__(self):
@@ -147,4 +143,4 @@
         self.shutdown()
       while not self.is_shutdown:
         self.poll()
-      grpc.grpc_completion_queue_destroy(self.c_completion_queue)
+      grpc_completion_queue_destroy(self.c_completion_queue)
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd.pxi
similarity index 76%
rename from src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd
rename to src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd.pxi
index db9f8dd..c793c8f 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd.pxi
@@ -29,27 +29,24 @@
 
 cimport cpython
 
-from grpc._cython._cygrpc cimport grpc
-from grpc._cython._cygrpc cimport records
-
 
 cdef class ChannelCredentials:
 
-  cdef grpc.grpc_channel_credentials *c_credentials
-  cdef grpc.grpc_ssl_pem_key_cert_pair c_ssl_pem_key_cert_pair
+  cdef grpc_channel_credentials *c_credentials
+  cdef grpc_ssl_pem_key_cert_pair c_ssl_pem_key_cert_pair
   cdef list references
 
 
 cdef class CallCredentials:
 
-  cdef grpc.grpc_call_credentials *c_credentials
+  cdef grpc_call_credentials *c_credentials
   cdef list references
 
 
 cdef class ServerCredentials:
 
-  cdef grpc.grpc_server_credentials *c_credentials
-  cdef grpc.grpc_ssl_pem_key_cert_pair *c_ssl_pem_key_cert_pairs
+  cdef grpc_server_credentials *c_credentials
+  cdef grpc_ssl_pem_key_cert_pair *c_ssl_pem_key_cert_pairs
   cdef size_t c_ssl_pem_key_cert_pairs_count
   cdef list references
 
@@ -59,16 +56,16 @@
   cdef object plugin_callback
   cdef str plugin_name
 
-  cdef grpc.grpc_metadata_credentials_plugin make_c_plugin(self)
+  cdef grpc_metadata_credentials_plugin make_c_plugin(self)
 
 
 cdef class AuthMetadataContext:
 
-  cdef grpc.grpc_auth_metadata_context context
+  cdef grpc_auth_metadata_context context
 
 
 cdef void plugin_get_metadata(
-    void *state, grpc.grpc_auth_metadata_context context,
-    grpc.grpc_credentials_plugin_metadata_cb cb, void *user_data) with gil
+    void *state, grpc_auth_metadata_context context,
+    grpc_credentials_plugin_metadata_cb cb, void *user_data) with gil
 
 cdef void plugin_destroy_c_plugin_state(void *state)
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
similarity index 82%
rename from src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx
rename to src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
index a968894..3f439c8 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
@@ -29,9 +29,6 @@
 
 cimport cpython
 
-from grpc._cython._cygrpc cimport grpc
-from grpc._cython._cygrpc cimport records
-
 
 cdef class ChannelCredentials:
 
@@ -49,7 +46,7 @@
 
   def __dealloc__(self):
     if self.c_credentials != NULL:
-      grpc.grpc_channel_credentials_release(self.c_credentials)
+      grpc_channel_credentials_release(self.c_credentials)
 
 
 cdef class CallCredentials:
@@ -66,7 +63,7 @@
 
   def __dealloc__(self):
     if self.c_credentials != NULL:
-      grpc.grpc_call_credentials_release(self.c_credentials)
+      grpc_call_credentials_release(self.c_credentials)
 
 
 cdef class ServerCredentials:
@@ -77,7 +74,7 @@
 
   def __dealloc__(self):
     if self.c_credentials != NULL:
-      grpc.grpc_server_credentials_release(self.c_credentials)
+      grpc_server_credentials_release(self.c_credentials)
 
 
 cdef class CredentialsMetadataPlugin:
@@ -86,8 +83,8 @@
     """
     Args:
       plugin_callback (callable): Callback accepting a service URL (str/bytes)
-        and callback object (accepting a records.Metadata,
-        grpc.grpc_status_code, and a str/bytes error message). This argument
+        and callback object (accepting a Metadata,
+        grpc_status_code, and a str/bytes error message). This argument
         when called should be non-blocking and eventually call the callback
         object with the appropriate status code/details and metadata (if
         successful).
@@ -99,8 +96,8 @@
     self.plugin_name = name
 
   @staticmethod
-  cdef grpc.grpc_metadata_credentials_plugin make_c_plugin(self):
-    cdef grpc.grpc_metadata_credentials_plugin result
+  cdef grpc_metadata_credentials_plugin make_c_plugin(self):
+    cdef grpc_metadata_credentials_plugin result
     result.get_metadata = plugin_get_metadata
     result.destroy = plugin_destroy_c_plugin_state
     result.state = <void *>self
@@ -125,10 +122,10 @@
 
 
 cdef void plugin_get_metadata(
-    void *state, grpc.grpc_auth_metadata_context context,
-    grpc.grpc_credentials_plugin_metadata_cb cb, void *user_data) with gil:
+    void *state, grpc_auth_metadata_context context,
+    grpc_credentials_plugin_metadata_cb cb, void *user_data) with gil:
   def python_callback(
-      records.Metadata metadata, grpc.grpc_status_code status,
+      Metadata metadata, grpc_status_code status,
       const char *error_details):
     cb(user_data, metadata.c_metadata_array.metadata,
        metadata.c_metadata_array.count, status, error_details)
@@ -142,11 +139,11 @@
 
 def channel_credentials_google_default():
   cdef ChannelCredentials credentials = ChannelCredentials();
-  credentials.c_credentials = grpc.grpc_google_default_credentials_create()
+  credentials.c_credentials = grpc_google_default_credentials_create()
   return credentials
 
 def channel_credentials_ssl(pem_root_certificates,
-                            records.SslPemKeyCertPair ssl_pem_key_cert_pair):
+                            SslPemKeyCertPair ssl_pem_key_cert_pair):
   if pem_root_certificates is None:
     pass
   elif isinstance(pem_root_certificates, bytes):
@@ -161,11 +158,11 @@
     c_pem_root_certificates = pem_root_certificates
     credentials.references.append(pem_root_certificates)
   if ssl_pem_key_cert_pair is not None:
-    credentials.c_credentials = grpc.grpc_ssl_credentials_create(
+    credentials.c_credentials = grpc_ssl_credentials_create(
         c_pem_root_certificates, &ssl_pem_key_cert_pair.c_pair, NULL)
     credentials.references.append(ssl_pem_key_cert_pair)
   else:
-    credentials.c_credentials = grpc.grpc_ssl_credentials_create(
+    credentials.c_credentials = grpc_ssl_credentials_create(
       c_pem_root_certificates, NULL, NULL)
   return credentials
 
@@ -175,7 +172,7 @@
   if not credentials_1.is_valid or not credentials_2.is_valid:
     raise ValueError("passed credentials must both be valid")
   cdef ChannelCredentials credentials = ChannelCredentials()
-  credentials.c_credentials = grpc.grpc_composite_channel_credentials_create(
+  credentials.c_credentials = grpc_composite_channel_credentials_create(
       credentials_1.c_credentials, credentials_2.c_credentials, NULL)
   credentials.references.append(credentials_1)
   credentials.references.append(credentials_2)
@@ -187,7 +184,7 @@
   if not credentials_1.is_valid or not credentials_2.is_valid:
     raise ValueError("passed credentials must both be valid")
   cdef CallCredentials credentials = CallCredentials()
-  credentials.c_credentials = grpc.grpc_composite_call_credentials_create(
+  credentials.c_credentials = grpc_composite_call_credentials_create(
       credentials_1.c_credentials, credentials_2.c_credentials, NULL)
   credentials.references.append(credentials_1)
   credentials.references.append(credentials_2)
@@ -196,11 +193,11 @@
 def call_credentials_google_compute_engine():
   cdef CallCredentials credentials = CallCredentials()
   credentials.c_credentials = (
-      grpc.grpc_google_compute_engine_credentials_create(NULL))
+      grpc_google_compute_engine_credentials_create(NULL))
   return credentials
 
 def call_credentials_service_account_jwt_access(
-    json_key, records.Timespec token_lifetime not None):
+    json_key, Timespec token_lifetime not None):
   if isinstance(json_key, bytes):
     pass
   elif isinstance(json_key, basestring):
@@ -209,7 +206,7 @@
     raise TypeError("expected json_key to be str or bytes")
   cdef CallCredentials credentials = CallCredentials()
   credentials.c_credentials = (
-      grpc.grpc_service_account_jwt_access_credentials_create(
+      grpc_service_account_jwt_access_credentials_create(
           json_key, token_lifetime.c_time, NULL))
   credentials.references.append(json_key)
   return credentials
@@ -222,7 +219,7 @@
   else:
     raise TypeError("expected json_refresh_token to be str or bytes")
   cdef CallCredentials credentials = CallCredentials()
-  credentials.c_credentials = grpc.grpc_google_refresh_token_credentials_create(
+  credentials.c_credentials = grpc_google_refresh_token_credentials_create(
       json_refresh_token, NULL)
   credentials.references.append(json_refresh_token)
   return credentials
@@ -241,7 +238,7 @@
   else:
     raise TypeError("expected authority_selector to be str or bytes")
   cdef CallCredentials credentials = CallCredentials()
-  credentials.c_credentials = grpc.grpc_google_iam_credentials_create(
+  credentials.c_credentials = grpc_google_iam_credentials_create(
       authorization_token, authority_selector, NULL)
   credentials.references.append(authorization_token)
   credentials.references.append(authority_selector)
@@ -250,7 +247,7 @@
 def call_credentials_metadata_plugin(CredentialsMetadataPlugin plugin):
   cdef CallCredentials credentials = CallCredentials()
   credentials.c_credentials = (
-      grpc.grpc_metadata_credentials_create_from_plugin(plugin.make_c_plugin(),
+      grpc_metadata_credentials_create_from_plugin(plugin.make_c_plugin(),
                                                         NULL))
   # TODO(atash): the following held reference is *probably* never necessary
   credentials.references.append(plugin)
@@ -270,22 +267,22 @@
     raise TypeError("expected pem_root_certs to be str or bytes")
   pem_key_cert_pairs = list(pem_key_cert_pairs)
   for pair in pem_key_cert_pairs:
-    if not isinstance(pair, records.SslPemKeyCertPair):
+    if not isinstance(pair, SslPemKeyCertPair):
       raise TypeError("expected pem_key_cert_pairs to be sequence of "
-                      "records.SslPemKeyCertPair")
+                      "SslPemKeyCertPair")
   cdef ServerCredentials credentials = ServerCredentials()
   credentials.references.append(pem_key_cert_pairs)
   credentials.references.append(pem_root_certs)
   credentials.c_ssl_pem_key_cert_pairs_count = len(pem_key_cert_pairs)
   credentials.c_ssl_pem_key_cert_pairs = (
-      <grpc.grpc_ssl_pem_key_cert_pair *>grpc.gpr_malloc(
-          sizeof(grpc.grpc_ssl_pem_key_cert_pair) *
+      <grpc_ssl_pem_key_cert_pair *>gpr_malloc(
+          sizeof(grpc_ssl_pem_key_cert_pair) *
               credentials.c_ssl_pem_key_cert_pairs_count
       ))
   for i in range(credentials.c_ssl_pem_key_cert_pairs_count):
     credentials.c_ssl_pem_key_cert_pairs[i] = (
-        (<records.SslPemKeyCertPair>pem_key_cert_pairs[i]).c_pair)
-  credentials.c_credentials = grpc.grpc_ssl_server_credentials_create(
+        (<SslPemKeyCertPair>pem_key_cert_pairs[i]).c_pair)
+  credentials.c_credentials = grpc_ssl_server_credentials_create(
       c_pem_root_certs, credentials.c_ssl_pem_key_cert_pairs,
       credentials.c_ssl_pem_key_cert_pairs_count, force_client_auth, NULL)
   return credentials
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
similarity index 100%
rename from src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxd
rename to src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd.pxi
similarity index 80%
rename from src/python/grpcio/grpc/_cython/_cygrpc/records.pxd
rename to src/python/grpcio/grpc/_cython/_cygrpc/records.pxd.pxi
index 4c844e4..3039781 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd.pxi
@@ -27,19 +27,15 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-from grpc._cython._cygrpc cimport grpc
-from grpc._cython._cygrpc cimport call
-from grpc._cython._cygrpc cimport server
-
 
 cdef class Timespec:
 
-  cdef grpc.gpr_timespec c_time
+  cdef gpr_timespec c_time
 
 
 cdef class CallDetails:
 
-  cdef grpc.grpc_call_details c_details
+  cdef grpc_call_details c_details
 
 
 cdef class OperationTag:
@@ -48,8 +44,8 @@
   cdef list references
   # This allows CompletionQueue to notify the Python Server object that the
   # underlying GRPC core server has shutdown
-  cdef server.Server shutting_down_server
-  cdef call.Call operation_call
+  cdef Server shutting_down_server
+  cdef Call operation_call
   cdef CallDetails request_call_details
   cdef Metadata request_metadata
   cdef Operations batch_operations
@@ -58,12 +54,12 @@
 
 cdef class Event:
 
-  cdef readonly grpc.grpc_completion_type type
+  cdef readonly grpc_completion_type type
   cdef readonly bint success
   cdef readonly object tag
 
   # For operations with calls
-  cdef readonly call.Call operation_call
+  cdef readonly Call operation_call
 
   # For Server.request_call
   cdef readonly bint is_new_request
@@ -76,45 +72,45 @@
 
 cdef class ByteBuffer:
 
-  cdef grpc.grpc_byte_buffer *c_byte_buffer
+  cdef grpc_byte_buffer *c_byte_buffer
 
 
 cdef class SslPemKeyCertPair:
 
-  cdef grpc.grpc_ssl_pem_key_cert_pair c_pair
+  cdef grpc_ssl_pem_key_cert_pair c_pair
   cdef readonly object private_key, certificate_chain
 
 
 cdef class ChannelArg:
 
-  cdef grpc.grpc_arg c_arg
+  cdef grpc_arg c_arg
   cdef readonly object key, value
 
 
 cdef class ChannelArgs:
 
-  cdef grpc.grpc_channel_args c_args
+  cdef grpc_channel_args c_args
   cdef list args
 
 
 cdef class Metadatum:
 
-  cdef grpc.grpc_metadata c_metadata
+  cdef grpc_metadata c_metadata
   cdef object _key, _value
 
 
 cdef class Metadata:
 
-  cdef grpc.grpc_metadata_array c_metadata_array
+  cdef grpc_metadata_array c_metadata_array
   cdef object metadata
 
 
 cdef class Operation:
 
-  cdef grpc.grpc_op c_op
+  cdef grpc_op c_op
   cdef ByteBuffer _received_message
   cdef Metadata _received_metadata
-  cdef grpc.grpc_status_code _received_status_code
+  cdef grpc_status_code _received_status_code
   cdef char *_received_status_details
   cdef size_t _received_status_details_capacity
   cdef int _received_cancelled
@@ -124,7 +120,7 @@
 
 cdef class Operations:
 
-  cdef grpc.grpc_op *c_ops
+  cdef grpc_op *c_ops
   cdef size_t c_nops
   cdef list operations
 
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
similarity index 70%
rename from src/python/grpcio/grpc/_cython/_cygrpc/records.pyx
rename to src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
index 79a7f8f..d7ad9e5 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
@@ -27,103 +27,99 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-from grpc._cython._cygrpc cimport grpc
-from grpc._cython._cygrpc cimport call
-from grpc._cython._cygrpc cimport server
-
 
 class ConnectivityState:
-  idle = grpc.GRPC_CHANNEL_IDLE
-  connecting = grpc.GRPC_CHANNEL_CONNECTING
-  ready = grpc.GRPC_CHANNEL_READY
-  transient_failure = grpc.GRPC_CHANNEL_TRANSIENT_FAILURE
-  fatal_failure = grpc.GRPC_CHANNEL_FATAL_FAILURE
+  idle = GRPC_CHANNEL_IDLE
+  connecting = GRPC_CHANNEL_CONNECTING
+  ready = GRPC_CHANNEL_READY
+  transient_failure = GRPC_CHANNEL_TRANSIENT_FAILURE
+  fatal_failure = GRPC_CHANNEL_FATAL_FAILURE
 
 
 class ChannelArgKey:
-  enable_census = grpc.GRPC_ARG_ENABLE_CENSUS
-  max_concurrent_streams = grpc.GRPC_ARG_MAX_CONCURRENT_STREAMS
-  max_message_length = grpc.GRPC_ARG_MAX_MESSAGE_LENGTH
-  http2_initial_sequence_number = grpc.GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER
-  default_authority = grpc.GRPC_ARG_DEFAULT_AUTHORITY
-  primary_user_agent_string = grpc.GRPC_ARG_PRIMARY_USER_AGENT_STRING
-  secondary_user_agent_string = grpc.GRPC_ARG_SECONDARY_USER_AGENT_STRING
-  ssl_target_name_override = grpc.GRPC_SSL_TARGET_NAME_OVERRIDE_ARG
+  enable_census = GRPC_ARG_ENABLE_CENSUS
+  max_concurrent_streams = GRPC_ARG_MAX_CONCURRENT_STREAMS
+  max_message_length = GRPC_ARG_MAX_MESSAGE_LENGTH
+  http2_initial_sequence_number = GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER
+  default_authority = GRPC_ARG_DEFAULT_AUTHORITY
+  primary_user_agent_string = GRPC_ARG_PRIMARY_USER_AGENT_STRING
+  secondary_user_agent_string = GRPC_ARG_SECONDARY_USER_AGENT_STRING
+  ssl_target_name_override = GRPC_SSL_TARGET_NAME_OVERRIDE_ARG
 
 
 class WriteFlag:
-  buffer_hint = grpc.GRPC_WRITE_BUFFER_HINT
-  no_compress = grpc.GRPC_WRITE_NO_COMPRESS
+  buffer_hint = GRPC_WRITE_BUFFER_HINT
+  no_compress = GRPC_WRITE_NO_COMPRESS
 
 
 class StatusCode:
-  ok = grpc.GRPC_STATUS_OK
-  cancelled = grpc.GRPC_STATUS_CANCELLED
-  unknown = grpc.GRPC_STATUS_UNKNOWN
-  invalid_argument = grpc.GRPC_STATUS_INVALID_ARGUMENT
-  deadline_exceeded = grpc.GRPC_STATUS_DEADLINE_EXCEEDED
-  not_found = grpc.GRPC_STATUS_NOT_FOUND
-  already_exists = grpc.GRPC_STATUS_ALREADY_EXISTS
-  permission_denied = grpc.GRPC_STATUS_PERMISSION_DENIED
-  unauthenticated = grpc.GRPC_STATUS_UNAUTHENTICATED
-  resource_exhausted = grpc.GRPC_STATUS_RESOURCE_EXHAUSTED
-  failed_precondition = grpc.GRPC_STATUS_FAILED_PRECONDITION
-  aborted = grpc.GRPC_STATUS_ABORTED
-  out_of_range = grpc.GRPC_STATUS_OUT_OF_RANGE
-  unimplemented = grpc.GRPC_STATUS_UNIMPLEMENTED
-  internal = grpc.GRPC_STATUS_INTERNAL
-  unavailable = grpc.GRPC_STATUS_UNAVAILABLE
-  data_loss = grpc.GRPC_STATUS_DATA_LOSS
+  ok = GRPC_STATUS_OK
+  cancelled = GRPC_STATUS_CANCELLED
+  unknown = GRPC_STATUS_UNKNOWN
+  invalid_argument = GRPC_STATUS_INVALID_ARGUMENT
+  deadline_exceeded = GRPC_STATUS_DEADLINE_EXCEEDED
+  not_found = GRPC_STATUS_NOT_FOUND
+  already_exists = GRPC_STATUS_ALREADY_EXISTS
+  permission_denied = GRPC_STATUS_PERMISSION_DENIED
+  unauthenticated = GRPC_STATUS_UNAUTHENTICATED
+  resource_exhausted = GRPC_STATUS_RESOURCE_EXHAUSTED
+  failed_precondition = GRPC_STATUS_FAILED_PRECONDITION
+  aborted = GRPC_STATUS_ABORTED
+  out_of_range = GRPC_STATUS_OUT_OF_RANGE
+  unimplemented = GRPC_STATUS_UNIMPLEMENTED
+  internal = GRPC_STATUS_INTERNAL
+  unavailable = GRPC_STATUS_UNAVAILABLE
+  data_loss = GRPC_STATUS_DATA_LOSS
 
 
 class CallError:
-  ok = grpc.GRPC_CALL_OK
-  error = grpc.GRPC_CALL_ERROR
-  not_on_server = grpc.GRPC_CALL_ERROR_NOT_ON_SERVER
-  not_on_client = grpc.GRPC_CALL_ERROR_NOT_ON_CLIENT
-  already_accepted = grpc.GRPC_CALL_ERROR_ALREADY_ACCEPTED
-  already_invoked = grpc.GRPC_CALL_ERROR_ALREADY_INVOKED
-  not_invoked = grpc.GRPC_CALL_ERROR_NOT_INVOKED
-  already_finished = grpc.GRPC_CALL_ERROR_ALREADY_FINISHED
-  too_many_operations = grpc.GRPC_CALL_ERROR_TOO_MANY_OPERATIONS
-  invalid_flags = grpc.GRPC_CALL_ERROR_INVALID_FLAGS
-  invalid_metadata = grpc.GRPC_CALL_ERROR_INVALID_METADATA
+  ok = GRPC_CALL_OK
+  error = GRPC_CALL_ERROR
+  not_on_server = GRPC_CALL_ERROR_NOT_ON_SERVER
+  not_on_client = GRPC_CALL_ERROR_NOT_ON_CLIENT
+  already_accepted = GRPC_CALL_ERROR_ALREADY_ACCEPTED
+  already_invoked = GRPC_CALL_ERROR_ALREADY_INVOKED
+  not_invoked = GRPC_CALL_ERROR_NOT_INVOKED
+  already_finished = GRPC_CALL_ERROR_ALREADY_FINISHED
+  too_many_operations = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS
+  invalid_flags = GRPC_CALL_ERROR_INVALID_FLAGS
+  invalid_metadata = GRPC_CALL_ERROR_INVALID_METADATA
 
 
 class CompletionType:
-  queue_shutdown = grpc.GRPC_QUEUE_SHUTDOWN
-  queue_timeout = grpc.GRPC_QUEUE_TIMEOUT
-  operation_complete = grpc.GRPC_OP_COMPLETE
+  queue_shutdown = GRPC_QUEUE_SHUTDOWN
+  queue_timeout = GRPC_QUEUE_TIMEOUT
+  operation_complete = GRPC_OP_COMPLETE
 
 
 class OperationType:
-  send_initial_metadata = grpc.GRPC_OP_SEND_INITIAL_METADATA
-  send_message = grpc.GRPC_OP_SEND_MESSAGE
-  send_close_from_client = grpc.GRPC_OP_SEND_CLOSE_FROM_CLIENT
-  send_status_from_server = grpc.GRPC_OP_SEND_STATUS_FROM_SERVER
-  receive_initial_metadata = grpc.GRPC_OP_RECV_INITIAL_METADATA
-  receive_message = grpc.GRPC_OP_RECV_MESSAGE
-  receive_status_on_client = grpc.GRPC_OP_RECV_STATUS_ON_CLIENT
-  receive_close_on_server = grpc.GRPC_OP_RECV_CLOSE_ON_SERVER
+  send_initial_metadata = GRPC_OP_SEND_INITIAL_METADATA
+  send_message = GRPC_OP_SEND_MESSAGE
+  send_close_from_client = GRPC_OP_SEND_CLOSE_FROM_CLIENT
+  send_status_from_server = GRPC_OP_SEND_STATUS_FROM_SERVER
+  receive_initial_metadata = GRPC_OP_RECV_INITIAL_METADATA
+  receive_message = GRPC_OP_RECV_MESSAGE
+  receive_status_on_client = GRPC_OP_RECV_STATUS_ON_CLIENT
+  receive_close_on_server = GRPC_OP_RECV_CLOSE_ON_SERVER
 
 
 cdef class Timespec:
 
   def __cinit__(self, time):
     if time is None:
-      self.c_time = grpc.gpr_now(grpc.GPR_CLOCK_REALTIME)
+      self.c_time = gpr_now(GPR_CLOCK_REALTIME)
       return
     if isinstance(time, int):
       time = float(time)
     if isinstance(time, float):
       if time == float("+inf"):
-        self.c_time = grpc.gpr_inf_future(grpc.GPR_CLOCK_REALTIME)
+        self.c_time = gpr_inf_future(GPR_CLOCK_REALTIME)
       elif time == float("-inf"):
-        self.c_time = grpc.gpr_inf_past(grpc.GPR_CLOCK_REALTIME)
+        self.c_time = gpr_inf_past(GPR_CLOCK_REALTIME)
       else:
         self.c_time.seconds = time
         self.c_time.nanoseconds = (time - float(self.c_time.seconds)) * 1e9
-        self.c_time.clock_type = grpc.GPR_CLOCK_REALTIME
+        self.c_time.clock_type = GPR_CLOCK_REALTIME
     elif isinstance(time, Timespec):
       self.c_time = (<Timespec>time).c_time
     else:
@@ -135,19 +131,19 @@
     # TODO(atash) ensure that everywhere a Timespec is created that it's
     # converted to GPR_CLOCK_REALTIME then and not every time someone wants to
     # read values off in Python.
-    cdef grpc.gpr_timespec real_time = (
-        grpc.gpr_convert_clock_type(self.c_time, grpc.GPR_CLOCK_REALTIME))
+    cdef gpr_timespec real_time = (
+        gpr_convert_clock_type(self.c_time, GPR_CLOCK_REALTIME))
     return real_time.seconds
 
   @property
   def nanoseconds(self):
-    cdef grpc.gpr_timespec real_time = (
-        grpc.gpr_convert_clock_type(self.c_time, grpc.GPR_CLOCK_REALTIME))
+    cdef gpr_timespec real_time = (
+        gpr_convert_clock_type(self.c_time, GPR_CLOCK_REALTIME))
     return real_time.nanoseconds
 
   def __float__(self):
-    cdef grpc.gpr_timespec real_time = (
-        grpc.gpr_convert_clock_type(self.c_time, grpc.GPR_CLOCK_REALTIME))
+    cdef gpr_timespec real_time = (
+        gpr_convert_clock_type(self.c_time, GPR_CLOCK_REALTIME))
     return <double>real_time.seconds + <double>real_time.nanoseconds / 1e9
 
   infinite_future = Timespec(float("+inf"))
@@ -157,10 +153,10 @@
 cdef class CallDetails:
 
   def __cinit__(self):
-    grpc.grpc_call_details_init(&self.c_details)
+    grpc_call_details_init(&self.c_details)
 
   def __dealloc__(self):
-    grpc.grpc_call_details_destroy(&self.c_details)
+    grpc_call_details_destroy(&self.c_details)
 
   @property
   def method(self):
@@ -192,8 +188,8 @@
 
 cdef class Event:
 
-  def __cinit__(self, grpc.grpc_completion_type type, bint success,
-                object tag, call.Call operation_call,
+  def __cinit__(self, grpc_completion_type type, bint success,
+                object tag, Call operation_call,
                 CallDetails request_call_details,
                 Metadata request_metadata,
                 bint is_new_request,
@@ -228,31 +224,31 @@
                       "ByteBuffer, not {}".format(type(data)))
 
     cdef char *c_data = data
-    data_slice = grpc.gpr_slice_from_copied_buffer(c_data, len(data))
-    self.c_byte_buffer = grpc.grpc_raw_byte_buffer_create(
+    data_slice = gpr_slice_from_copied_buffer(c_data, len(data))
+    self.c_byte_buffer = grpc_raw_byte_buffer_create(
         &data_slice, 1)
-    grpc.gpr_slice_unref(data_slice)
+    gpr_slice_unref(data_slice)
 
   def bytes(self):
-    cdef grpc.grpc_byte_buffer_reader reader
-    cdef grpc.gpr_slice data_slice
+    cdef grpc_byte_buffer_reader reader
+    cdef gpr_slice data_slice
     cdef size_t data_slice_length
     cdef void *data_slice_pointer
     if self.c_byte_buffer != NULL:
-      grpc.grpc_byte_buffer_reader_init(&reader, self.c_byte_buffer)
+      grpc_byte_buffer_reader_init(&reader, self.c_byte_buffer)
       result = b""
-      while grpc.grpc_byte_buffer_reader_next(&reader, &data_slice):
-        data_slice_pointer = grpc.gpr_slice_start_ptr(data_slice)
-        data_slice_length = grpc.gpr_slice_length(data_slice)
+      while grpc_byte_buffer_reader_next(&reader, &data_slice):
+        data_slice_pointer = gpr_slice_start_ptr(data_slice)
+        data_slice_length = gpr_slice_length(data_slice)
         result += (<char *>data_slice_pointer)[:data_slice_length]
-      grpc.grpc_byte_buffer_reader_destroy(&reader)
+      grpc_byte_buffer_reader_destroy(&reader)
       return result
     else:
       return None
 
   def __len__(self):
     if self.c_byte_buffer != NULL:
-      return grpc.grpc_byte_buffer_length(self.c_byte_buffer)
+      return grpc_byte_buffer_length(self.c_byte_buffer)
     else:
       return 0
 
@@ -261,7 +257,7 @@
 
   def __dealloc__(self):
     if self.c_byte_buffer != NULL:
-      grpc.grpc_byte_buffer_destroy(self.c_byte_buffer)
+      grpc_byte_buffer_destroy(self.c_byte_buffer)
 
 
 cdef class SslPemKeyCertPair:
@@ -295,15 +291,15 @@
       raise TypeError("expected key to be of type str or bytes")
     if isinstance(value, bytes):
       self.value = value
-      self.c_arg.type = grpc.GRPC_ARG_STRING
+      self.c_arg.type = GRPC_ARG_STRING
       self.c_arg.value.string = self.value
     elif isinstance(value, basestring):
       self.value = value.encode()
-      self.c_arg.type = grpc.GRPC_ARG_STRING
+      self.c_arg.type = GRPC_ARG_STRING
       self.c_arg.value.string = self.value
     elif isinstance(value, int):
       self.value = int(value)
-      self.c_arg.type = grpc.GRPC_ARG_INTEGER
+      self.c_arg.type = GRPC_ARG_INTEGER
       self.c_arg.value.integer = self.value
     else:
       raise TypeError("expected value to be of type str or bytes or int")
@@ -318,14 +314,14 @@
       if not isinstance(arg, ChannelArg):
         raise TypeError("expected list of ChannelArg")
     self.c_args.arguments_length = len(self.args)
-    self.c_args.arguments = <grpc.grpc_arg *>grpc.gpr_malloc(
-        self.c_args.arguments_length*sizeof(grpc.grpc_arg)
+    self.c_args.arguments = <grpc_arg *>gpr_malloc(
+        self.c_args.arguments_length*sizeof(grpc_arg)
     )
     for i in range(self.c_args.arguments_length):
       self.c_args.arguments[i] = (<ChannelArg>self.args[i]).c_arg
 
   def __dealloc__(self):
-    grpc.gpr_free(self.c_args.arguments)
+    gpr_free(self.c_args.arguments)
 
   def __len__(self):
     # self.args is never stale; it's only updated from this file
@@ -406,11 +402,11 @@
     for metadatum in metadata:
       if not isinstance(metadatum, Metadatum):
         raise TypeError("expected list of Metadatum")
-    grpc.grpc_metadata_array_init(&self.c_metadata_array)
+    grpc_metadata_array_init(&self.c_metadata_array)
     self.c_metadata_array.count = len(self.metadata)
     self.c_metadata_array.capacity = len(self.metadata)
-    self.c_metadata_array.metadata = <grpc.grpc_metadata *>grpc.gpr_malloc(
-        self.c_metadata_array.count*sizeof(grpc.grpc_metadata)
+    self.c_metadata_array.metadata = <grpc_metadata *>gpr_malloc(
+        self.c_metadata_array.count*sizeof(grpc_metadata)
     )
     for i in range(self.c_metadata_array.count):
       self.c_metadata_array.metadata[i] = (
@@ -420,7 +416,7 @@
     # this frees the allocated memory for the grpc_metadata_array (although
     # it'd be nice if that were documented somewhere...) TODO(atash): document
     # this in the C core
-    grpc.grpc_metadata_array_destroy(&self.c_metadata_array)
+    grpc_metadata_array_destroy(&self.c_metadata_array)
 
   def __len__(self):
     return self.c_metadata_array.count
@@ -449,49 +445,49 @@
 
   @property
   def has_status(self):
-    return self.c_op.type == grpc.GRPC_OP_RECV_STATUS_ON_CLIENT
+    return self.c_op.type == GRPC_OP_RECV_STATUS_ON_CLIENT
 
   @property
   def received_message(self):
-    if self.c_op.type != grpc.GRPC_OP_RECV_MESSAGE:
+    if self.c_op.type != GRPC_OP_RECV_MESSAGE:
       raise TypeError("self must be an operation receiving a message")
     return self._received_message
 
   @property
   def received_message_or_none(self):
-    if self.c_op.type != grpc.GRPC_OP_RECV_MESSAGE:
+    if self.c_op.type != GRPC_OP_RECV_MESSAGE:
       return None
     return self._received_message
 
   @property
   def received_metadata(self):
-    if (self.c_op.type != grpc.GRPC_OP_RECV_INITIAL_METADATA and
-        self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT):
+    if (self.c_op.type != GRPC_OP_RECV_INITIAL_METADATA and
+        self.c_op.type != GRPC_OP_RECV_STATUS_ON_CLIENT):
       raise TypeError("self must be an operation receiving metadata")
     return self._received_metadata
 
   @property
   def received_metadata_or_none(self):
-    if (self.c_op.type != grpc.GRPC_OP_RECV_INITIAL_METADATA and
-        self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT):
+    if (self.c_op.type != GRPC_OP_RECV_INITIAL_METADATA and
+        self.c_op.type != GRPC_OP_RECV_STATUS_ON_CLIENT):
       return None
     return self._received_metadata
 
   @property
   def received_status_code(self):
-    if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT:
+    if self.c_op.type != GRPC_OP_RECV_STATUS_ON_CLIENT:
       raise TypeError("self must be an operation receiving a status code")
     return self._received_status_code
 
   @property
   def received_status_code_or_none(self):
-    if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT:
+    if self.c_op.type != GRPC_OP_RECV_STATUS_ON_CLIENT:
       return None
     return self._received_status_code
 
   @property
   def received_status_details(self):
-    if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT:
+    if self.c_op.type != GRPC_OP_RECV_STATUS_ON_CLIENT:
       raise TypeError("self must be an operation receiving status details")
     if self._received_status_details:
       return self._received_status_details
@@ -500,7 +496,7 @@
 
   @property
   def received_status_details_or_none(self):
-    if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT:
+    if self.c_op.type != GRPC_OP_RECV_STATUS_ON_CLIENT:
       return None
     if self._received_status_details:
       return self._received_status_details
@@ -509,14 +505,14 @@
 
   @property
   def received_cancelled(self):
-    if self.c_op.type != grpc.GRPC_OP_RECV_CLOSE_ON_SERVER:
+    if self.c_op.type != GRPC_OP_RECV_CLOSE_ON_SERVER:
       raise TypeError("self must be an operation receiving cancellation "
                       "information")
     return False if self._received_cancelled == 0 else True
 
   @property
   def received_cancelled_or_none(self):
-    if self.c_op.type != grpc.GRPC_OP_RECV_CLOSE_ON_SERVER:
+    if self.c_op.type != GRPC_OP_RECV_CLOSE_ON_SERVER:
       return None
     return False if self._received_cancelled == 0 else True
 
@@ -524,12 +520,12 @@
     # We *almost* don't need to do anything; most of the objects are handled by
     # Python. The remaining one(s) are primitive fields filled in by GRPC core.
     # This means that we need to clean up after receive_status_on_client.
-    if self.c_op.type == grpc.GRPC_OP_RECV_STATUS_ON_CLIENT:
-      grpc.gpr_free(self._received_status_details)
+    if self.c_op.type == GRPC_OP_RECV_STATUS_ON_CLIENT:
+      gpr_free(self._received_status_details)
 
 def operation_send_initial_metadata(Metadata metadata):
   cdef Operation op = Operation()
-  op.c_op.type = grpc.GRPC_OP_SEND_INITIAL_METADATA
+  op.c_op.type = GRPC_OP_SEND_INITIAL_METADATA
   op.c_op.data.send_initial_metadata.count = metadata.c_metadata_array.count
   op.c_op.data.send_initial_metadata.metadata = (
       metadata.c_metadata_array.metadata)
@@ -539,7 +535,7 @@
 
 def operation_send_message(data):
   cdef Operation op = Operation()
-  op.c_op.type = grpc.GRPC_OP_SEND_MESSAGE
+  op.c_op.type = GRPC_OP_SEND_MESSAGE
   byte_buffer = ByteBuffer(data)
   op.c_op.data.send_message = byte_buffer.c_byte_buffer
   op.references.append(byte_buffer)
@@ -548,12 +544,12 @@
 
 def operation_send_close_from_client():
   cdef Operation op = Operation()
-  op.c_op.type = grpc.GRPC_OP_SEND_CLOSE_FROM_CLIENT
+  op.c_op.type = GRPC_OP_SEND_CLOSE_FROM_CLIENT
   op.is_valid = True
   return op
 
 def operation_send_status_from_server(
-    Metadata metadata, grpc.grpc_status_code code, details):
+    Metadata metadata, grpc_status_code code, details):
   if isinstance(details, bytes):
     pass
   elif isinstance(details, basestring):
@@ -561,7 +557,7 @@
   else:
     raise TypeError("expected a str or bytes object for details")
   cdef Operation op = Operation()
-  op.c_op.type = grpc.GRPC_OP_SEND_STATUS_FROM_SERVER
+  op.c_op.type = GRPC_OP_SEND_STATUS_FROM_SERVER
   op.c_op.data.send_status_from_server.trailing_metadata_count = (
       metadata.c_metadata_array.count)
   op.c_op.data.send_status_from_server.trailing_metadata = (
@@ -575,7 +571,7 @@
 
 def operation_receive_initial_metadata():
   cdef Operation op = Operation()
-  op.c_op.type = grpc.GRPC_OP_RECV_INITIAL_METADATA
+  op.c_op.type = GRPC_OP_RECV_INITIAL_METADATA
   op._received_metadata = Metadata([])
   op.c_op.data.receive_initial_metadata = (
       &op._received_metadata.c_metadata_array)
@@ -584,7 +580,7 @@
 
 def operation_receive_message():
   cdef Operation op = Operation()
-  op.c_op.type = grpc.GRPC_OP_RECV_MESSAGE
+  op.c_op.type = GRPC_OP_RECV_MESSAGE
   op._received_message = ByteBuffer(None)
   # n.b. the c_op.data.receive_message field needs to be deleted by us,
   # anyway, so we just let that be handled by the ByteBuffer() we allocated
@@ -595,7 +591,7 @@
 
 def operation_receive_status_on_client():
   cdef Operation op = Operation()
-  op.c_op.type = grpc.GRPC_OP_RECV_STATUS_ON_CLIENT
+  op.c_op.type = GRPC_OP_RECV_STATUS_ON_CLIENT
   op._received_metadata = Metadata([])
   op.c_op.data.receive_status_on_client.trailing_metadata = (
       &op._received_metadata.c_metadata_array)
@@ -610,7 +606,7 @@
 
 def operation_receive_close_on_server():
   cdef Operation op = Operation()
-  op.c_op.type = grpc.GRPC_OP_RECV_CLOSE_ON_SERVER
+  op.c_op.type = GRPC_OP_RECV_CLOSE_ON_SERVER
   op.c_op.data.receive_close_on_server.cancelled = &op._received_cancelled
   op.is_valid = True
   return op
@@ -647,8 +643,8 @@
       if not isinstance(operation, Operation):
         raise TypeError("expected operations to be iterable of Operation")
     self.c_nops = len(self.operations)
-    self.c_ops = <grpc.grpc_op *>grpc.gpr_malloc(
-        sizeof(grpc.grpc_op)*self.c_nops)
+    self.c_ops = <grpc_op *>gpr_malloc(
+        sizeof(grpc_op)*self.c_nops)
     for i in range(self.c_nops):
       self.c_ops[i] = (<Operation>(self.operations[i])).c_op
 
@@ -660,7 +656,7 @@
     return self.operations[i]
 
   def __dealloc__(self):
-    grpc.gpr_free(self.c_ops)
+    gpr_free(self.c_ops)
 
   def __iter__(self):
     return _OperationsIterator(self)
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/server.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/server.pxd.pxi
similarity index 90%
rename from src/python/grpcio/grpc/_cython/_cygrpc/server.pxd
rename to src/python/grpcio/grpc/_cython/_cygrpc/server.pxd.pxi
index 0257542..9db49e4 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/server.pxd
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/server.pxd.pxi
@@ -27,18 +27,15 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-from grpc._cython._cygrpc cimport grpc
-from grpc._cython._cygrpc cimport completion_queue
-
 
 cdef class Server:
 
-  cdef grpc.grpc_server *c_server
+  cdef grpc_server *c_server
   cdef bint is_started  # start has been called
   cdef bint is_shutting_down  # shutdown has been called
   cdef bint is_shutdown  # notification of complete shutdown received
   # used at dealloc when user forgets to shutdown
-  cdef completion_queue.CompletionQueue backup_shutdown_queue
+  cdef CompletionQueue backup_shutdown_queue
   cdef list references
   cdef list registered_completion_queues
 
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
similarity index 74%
rename from src/python/grpcio/grpc/_cython/_cygrpc/server.pyx
rename to src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
index b0bafbc..8b65935 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
@@ -29,45 +29,39 @@
 
 cimport cpython
 
-from grpc._cython._cygrpc cimport call
-from grpc._cython._cygrpc cimport completion_queue
-from grpc._cython._cygrpc cimport credentials
-from grpc._cython._cygrpc cimport grpc
-from grpc._cython._cygrpc cimport records
-
 import time
 
 
 cdef class Server:
 
-  def __cinit__(self, records.ChannelArgs arguments=None):
-    cdef grpc.grpc_channel_args *c_arguments = NULL
+  def __cinit__(self, ChannelArgs arguments=None):
+    cdef grpc_channel_args *c_arguments = NULL
     self.references = []
     self.registered_completion_queues = []
     if arguments is not None:
       c_arguments = &arguments.c_args
       self.references.append(arguments)
-    self.c_server = grpc.grpc_server_create(c_arguments, NULL)
+    self.c_server = grpc_server_create(c_arguments, NULL)
     self.is_started = False
     self.is_shutting_down = False
     self.is_shutdown = False
 
   def request_call(
-      self, completion_queue.CompletionQueue call_queue not None,
-      completion_queue.CompletionQueue server_queue not None, tag):
+      self, CompletionQueue call_queue not None,
+      CompletionQueue server_queue not None, tag):
     if not self.is_started or self.is_shutting_down:
       raise ValueError("server must be started and not shutting down")
     if server_queue not in self.registered_completion_queues:
       raise ValueError("server_queue must be a registered completion queue")
-    cdef records.OperationTag operation_tag = records.OperationTag(tag)
-    operation_tag.operation_call = call.Call()
-    operation_tag.request_call_details = records.CallDetails()
-    operation_tag.request_metadata = records.Metadata([])
+    cdef OperationTag operation_tag = OperationTag(tag)
+    operation_tag.operation_call = Call()
+    operation_tag.request_call_details = CallDetails()
+    operation_tag.request_metadata = Metadata([])
     operation_tag.references.extend([self, call_queue, server_queue])
     operation_tag.is_new_request = True
-    operation_tag.batch_operations = records.Operations([])
+    operation_tag.batch_operations = Operations([])
     cpython.Py_INCREF(operation_tag)
-    return grpc.grpc_server_request_call(
+    return grpc_server_request_call(
         self.c_server, &operation_tag.operation_call.c_call,
         &operation_tag.request_call_details.c_details,
         &operation_tag.request_metadata.c_metadata_array,
@@ -75,25 +69,25 @@
         <cpython.PyObject *>operation_tag)
 
   def register_completion_queue(
-      self, completion_queue.CompletionQueue queue not None):
+      self, CompletionQueue queue not None):
     if self.is_started:
       raise ValueError("cannot register completion queues after start")
-    grpc.grpc_server_register_completion_queue(
+    grpc_server_register_completion_queue(
         self.c_server, queue.c_completion_queue, NULL)
     self.registered_completion_queues.append(queue)
 
   def start(self):
     if self.is_started:
       raise ValueError("the server has already started")
-    self.backup_shutdown_queue = completion_queue.CompletionQueue()
+    self.backup_shutdown_queue = CompletionQueue()
     self.register_completion_queue(self.backup_shutdown_queue)
     self.is_started = True
-    grpc.grpc_server_start(self.c_server)
+    grpc_server_start(self.c_server)
     # Ensure the core has gotten a chance to do the start-up work
-    self.backup_shutdown_queue.pluck(None, records.Timespec(None))
+    self.backup_shutdown_queue.pluck(None, Timespec(None))
 
   def add_http2_port(self, address,
-                     credentials.ServerCredentials server_credentials=None):
+                     ServerCredentials server_credentials=None):
     if isinstance(address, bytes):
       pass
     elif isinstance(address, basestring):
@@ -103,13 +97,13 @@
     self.references.append(address)
     if server_credentials is not None:
       self.references.append(server_credentials)
-      return grpc.grpc_server_add_secure_http2_port(
+      return grpc_server_add_secure_http2_port(
           self.c_server, address, server_credentials.c_credentials)
     else:
-      return grpc.grpc_server_add_insecure_http2_port(self.c_server, address)
+      return grpc_server_add_insecure_http2_port(self.c_server, address)
 
-  def shutdown(self, completion_queue.CompletionQueue queue not None, tag):
-    cdef records.OperationTag operation_tag
+  def shutdown(self, CompletionQueue queue not None, tag):
+    cdef OperationTag operation_tag
     if queue.is_shutting_down:
       raise ValueError("queue must be live")
     elif not self.is_started:
@@ -120,11 +114,11 @@
       raise ValueError("expected registered completion queue")
     else:
       self.is_shutting_down = True
-      operation_tag = records.OperationTag(tag)
+      operation_tag = OperationTag(tag)
       operation_tag.shutting_down_server = self
       operation_tag.references.extend([self, queue])
       cpython.Py_INCREF(operation_tag)
-      grpc.grpc_server_shutdown_and_notify(
+      grpc_server_shutdown_and_notify(
           self.c_server, queue.c_completion_queue,
           <cpython.PyObject *>operation_tag)
 
@@ -138,7 +132,7 @@
     elif self.is_shutdown:
       return
     else:
-      grpc.grpc_server_cancel_all_calls(self.c_server)
+      grpc_server_cancel_all_calls(self.c_server)
 
   def __dealloc__(self):
     if self.c_server != NULL:
@@ -157,5 +151,5 @@
         # much but repeatedly release the GIL and wait
         while not self.is_shutdown:
           time.sleep(0)
-      grpc.grpc_server_destroy(self.c_server)
+      grpc_server_destroy(self.c_server)
 
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd b/src/python/grpcio/grpc/_cython/cygrpc.pxd
similarity index 79%
copy from src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd
copy to src/python/grpcio/grpc/_cython/cygrpc.pxd
index 3e341bf..f22346c 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd
+++ b/src/python/grpcio/grpc/_cython/cygrpc.pxd
@@ -1,4 +1,4 @@
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -27,10 +27,11 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-from grpc._cython._cygrpc cimport grpc
+include "grpc/_cython/_cygrpc/grpc.pxi"
 
-
-cdef class Channel:
-
-  cdef grpc.grpc_channel *c_channel
-  cdef list references
+include "grpc/_cython/_cygrpc/call.pxd.pxi"
+include "grpc/_cython/_cygrpc/channel.pxd.pxi"
+include "grpc/_cython/_cygrpc/credentials.pxd.pxi"
+include "grpc/_cython/_cygrpc/completion_queue.pxd.pxi"
+include "grpc/_cython/_cygrpc/records.pxd.pxi"
+include "grpc/_cython/_cygrpc/server.pxd.pxi"
diff --git a/src/python/grpcio/grpc/_cython/cygrpc.pyx b/src/python/grpcio/grpc/_cython/cygrpc.pyx
index 16ec12d..297c800 100644
--- a/src/python/grpcio/grpc/_cython/cygrpc.pyx
+++ b/src/python/grpcio/grpc/_cython/cygrpc.pyx
@@ -29,78 +29,14 @@
 
 cimport cpython
 
-from grpc._cython._cygrpc cimport grpc
-from grpc._cython._cygrpc cimport call
-from grpc._cython._cygrpc cimport channel
-from grpc._cython._cygrpc cimport credentials
-from grpc._cython._cygrpc cimport completion_queue
-from grpc._cython._cygrpc cimport records
-from grpc._cython._cygrpc cimport server
-
-from grpc._cython._cygrpc import call
-from grpc._cython._cygrpc import channel
-from grpc._cython._cygrpc import credentials
-from grpc._cython._cygrpc import completion_queue
-from grpc._cython._cygrpc import records
-from grpc._cython._cygrpc import server
-
-ConnectivityState = records.ConnectivityState
-ChannelArgKey = records.ChannelArgKey
-WriteFlag = records.WriteFlag
-StatusCode = records.StatusCode
-CallError = records.CallError
-CompletionType = records.CompletionType
-OperationType = records.OperationType
-Timespec = records.Timespec
-CallDetails = records.CallDetails
-Event = records.Event
-ByteBuffer = records.ByteBuffer
-SslPemKeyCertPair = records.SslPemKeyCertPair
-ChannelArg = records.ChannelArg
-ChannelArgs = records.ChannelArgs
-Metadatum = records.Metadatum
-Metadata = records.Metadata
-Operation = records.Operation
-
-operation_send_initial_metadata = records.operation_send_initial_metadata
-operation_send_message = records.operation_send_message
-operation_send_close_from_client = records.operation_send_close_from_client
-operation_send_status_from_server = records.operation_send_status_from_server
-operation_receive_initial_metadata = records.operation_receive_initial_metadata
-operation_receive_message = records.operation_receive_message
-operation_receive_status_on_client = records.operation_receive_status_on_client
-operation_receive_close_on_server = records.operation_receive_close_on_server
-
-Operations = records.Operations
-
-CallCredentials = credentials.CallCredentials
-ChannelCredentials = credentials.ChannelCredentials
-ServerCredentials = credentials.ServerCredentials
-CredentialsMetadataPlugin = credentials.CredentialsMetadataPlugin
-AuthMetadataContext = credentials.AuthMetadataContext
-
-channel_credentials_google_default = (
-    credentials.channel_credentials_google_default)
-channel_credentials_ssl = credentials.channel_credentials_ssl
-channel_credentials_composite = (
-    credentials.channel_credentials_composite)
-call_credentials_composite = (
-    credentials.call_credentials_composite)
-call_credentials_google_compute_engine = (
-    credentials.call_credentials_google_compute_engine)
-call_credentials_jwt_access = (
-    credentials.call_credentials_service_account_jwt_access)
-call_credentials_refresh_token = (
-    credentials.call_credentials_google_refresh_token)
-call_credentials_google_iam = credentials.call_credentials_google_iam
-call_credentials_metadata_plugin = credentials.call_credentials_metadata_plugin
-server_credentials_ssl = credentials.server_credentials_ssl
-
-CompletionQueue = completion_queue.CompletionQueue
-Channel = channel.Channel
-Server = server.Server
-Call = call.Call
-
+# TODO(atash): figure out why the coverage tool gets confused about the Cython
+# coverage plugin when the following files don't have a '.pxi' suffix.
+include "grpc/_cython/_cygrpc/call.pyx.pxi"
+include "grpc/_cython/_cygrpc/channel.pyx.pxi"
+include "grpc/_cython/_cygrpc/credentials.pyx.pxi"
+include "grpc/_cython/_cygrpc/completion_queue.pyx.pxi"
+include "grpc/_cython/_cygrpc/records.pyx.pxi"
+include "grpc/_cython/_cygrpc/server.pyx.pxi"
 
 #
 # Global state
@@ -109,10 +45,10 @@
 cdef class _ModuleState:
 
   def __cinit__(self):
-    grpc.grpc_init()
+    grpc_init()
 
   def __dealloc__(self):
-    grpc.grpc_shutdown()
+    grpc_shutdown()
 
 _module_state = _ModuleState()
 
diff --git a/src/python/grpcio/grpc/framework/interfaces/face/face.py b/src/python/grpcio/grpc/framework/interfaces/face/face.py
index 59da83d..404c3a7 100644
--- a/src/python/grpcio/grpc/framework/interfaces/face/face.py
+++ b/src/python/grpcio/grpc/framework/interfaces/face/face.py
@@ -1,4 +1,4 @@
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py
new file mode 100644
index 0000000..952898d
--- /dev/null
+++ b/src/python/grpcio/grpc_core_dependencies.py
@@ -0,0 +1,519 @@
+# Copyright 2015-2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio/grpc_core_dependencies.py.template`!!!
+
+CORE_SOURCE_FILES = [
+  'src/core/profiling/basic_timers.c',
+  'src/core/profiling/stap_timers.c',
+  'src/core/support/alloc.c',
+  'src/core/support/avl.c',
+  'src/core/support/cmdline.c',
+  'src/core/support/cpu_iphone.c',
+  'src/core/support/cpu_linux.c',
+  'src/core/support/cpu_posix.c',
+  'src/core/support/cpu_windows.c',
+  'src/core/support/env_linux.c',
+  'src/core/support/env_posix.c',
+  'src/core/support/env_win32.c',
+  'src/core/support/file.c',
+  'src/core/support/file_posix.c',
+  'src/core/support/file_win32.c',
+  'src/core/support/histogram.c',
+  'src/core/support/host_port.c',
+  'src/core/support/log.c',
+  'src/core/support/log_android.c',
+  'src/core/support/log_linux.c',
+  'src/core/support/log_posix.c',
+  'src/core/support/log_win32.c',
+  'src/core/support/murmur_hash.c',
+  'src/core/support/slice.c',
+  'src/core/support/slice_buffer.c',
+  'src/core/support/stack_lockfree.c',
+  'src/core/support/string.c',
+  'src/core/support/string_posix.c',
+  'src/core/support/string_win32.c',
+  'src/core/support/subprocess_posix.c',
+  'src/core/support/sync.c',
+  'src/core/support/sync_posix.c',
+  'src/core/support/sync_win32.c',
+  'src/core/support/thd.c',
+  'src/core/support/thd_posix.c',
+  'src/core/support/thd_win32.c',
+  'src/core/support/time.c',
+  'src/core/support/time_posix.c',
+  'src/core/support/time_precise.c',
+  'src/core/support/time_win32.c',
+  'src/core/support/tls_pthread.c',
+  'src/core/httpcli/httpcli_security_connector.c',
+  'src/core/security/base64.c',
+  'src/core/security/client_auth_filter.c',
+  'src/core/security/credentials.c',
+  'src/core/security/credentials_metadata.c',
+  'src/core/security/credentials_posix.c',
+  'src/core/security/credentials_win32.c',
+  'src/core/security/google_default_credentials.c',
+  'src/core/security/handshake.c',
+  'src/core/security/json_token.c',
+  'src/core/security/jwt_verifier.c',
+  'src/core/security/secure_endpoint.c',
+  'src/core/security/security_connector.c',
+  'src/core/security/security_context.c',
+  'src/core/security/server_auth_filter.c',
+  'src/core/security/server_secure_chttp2.c',
+  'src/core/surface/init_secure.c',
+  'src/core/surface/secure_channel_create.c',
+  'src/core/tsi/fake_transport_security.c',
+  'src/core/tsi/ssl_transport_security.c',
+  'src/core/tsi/transport_security.c',
+  'src/core/census/grpc_context.c',
+  'src/core/census/grpc_filter.c',
+  'src/core/channel/channel_args.c',
+  'src/core/channel/channel_stack.c',
+  'src/core/channel/client_channel.c',
+  'src/core/channel/client_uchannel.c',
+  'src/core/channel/compress_filter.c',
+  'src/core/channel/connected_channel.c',
+  'src/core/channel/http_client_filter.c',
+  'src/core/channel/http_server_filter.c',
+  'src/core/channel/subchannel_call_holder.c',
+  'src/core/client_config/client_config.c',
+  'src/core/client_config/connector.c',
+  'src/core/client_config/default_initial_connect_string.c',
+  'src/core/client_config/initial_connect_string.c',
+  'src/core/client_config/lb_policies/pick_first.c',
+  'src/core/client_config/lb_policies/round_robin.c',
+  'src/core/client_config/lb_policy.c',
+  'src/core/client_config/lb_policy_factory.c',
+  'src/core/client_config/lb_policy_registry.c',
+  'src/core/client_config/resolver.c',
+  'src/core/client_config/resolver_factory.c',
+  'src/core/client_config/resolver_registry.c',
+  'src/core/client_config/resolvers/dns_resolver.c',
+  'src/core/client_config/resolvers/sockaddr_resolver.c',
+  'src/core/client_config/subchannel.c',
+  'src/core/client_config/subchannel_factory.c',
+  'src/core/client_config/uri_parser.c',
+  'src/core/compression/algorithm.c',
+  'src/core/compression/message_compress.c',
+  'src/core/debug/trace.c',
+  'src/core/httpcli/format_request.c',
+  'src/core/httpcli/httpcli.c',
+  'src/core/httpcli/parser.c',
+  'src/core/iomgr/closure.c',
+  'src/core/iomgr/endpoint.c',
+  'src/core/iomgr/endpoint_pair_posix.c',
+  'src/core/iomgr/endpoint_pair_windows.c',
+  'src/core/iomgr/exec_ctx.c',
+  'src/core/iomgr/executor.c',
+  'src/core/iomgr/fd_posix.c',
+  'src/core/iomgr/iocp_windows.c',
+  'src/core/iomgr/iomgr.c',
+  'src/core/iomgr/iomgr_posix.c',
+  'src/core/iomgr/iomgr_windows.c',
+  'src/core/iomgr/pollset_multipoller_with_epoll.c',
+  'src/core/iomgr/pollset_multipoller_with_poll_posix.c',
+  'src/core/iomgr/pollset_posix.c',
+  'src/core/iomgr/pollset_set_posix.c',
+  'src/core/iomgr/pollset_set_windows.c',
+  'src/core/iomgr/pollset_windows.c',
+  'src/core/iomgr/resolve_address_posix.c',
+  'src/core/iomgr/resolve_address_windows.c',
+  'src/core/iomgr/sockaddr_utils.c',
+  'src/core/iomgr/socket_utils_common_posix.c',
+  'src/core/iomgr/socket_utils_linux.c',
+  'src/core/iomgr/socket_utils_posix.c',
+  'src/core/iomgr/socket_windows.c',
+  'src/core/iomgr/tcp_client_posix.c',
+  'src/core/iomgr/tcp_client_windows.c',
+  'src/core/iomgr/tcp_posix.c',
+  'src/core/iomgr/tcp_server_posix.c',
+  'src/core/iomgr/tcp_server_windows.c',
+  'src/core/iomgr/tcp_windows.c',
+  'src/core/iomgr/time_averaged_stats.c',
+  'src/core/iomgr/timer.c',
+  'src/core/iomgr/timer_heap.c',
+  'src/core/iomgr/udp_server.c',
+  'src/core/iomgr/wakeup_fd_eventfd.c',
+  'src/core/iomgr/wakeup_fd_nospecial.c',
+  'src/core/iomgr/wakeup_fd_pipe.c',
+  'src/core/iomgr/wakeup_fd_posix.c',
+  'src/core/iomgr/workqueue_posix.c',
+  'src/core/iomgr/workqueue_windows.c',
+  'src/core/json/json.c',
+  'src/core/json/json_reader.c',
+  'src/core/json/json_string.c',
+  'src/core/json/json_writer.c',
+  'src/core/surface/api_trace.c',
+  'src/core/surface/byte_buffer.c',
+  'src/core/surface/byte_buffer_reader.c',
+  'src/core/surface/call.c',
+  'src/core/surface/call_details.c',
+  'src/core/surface/call_log_batch.c',
+  'src/core/surface/channel.c',
+  'src/core/surface/channel_connectivity.c',
+  'src/core/surface/channel_create.c',
+  'src/core/surface/channel_ping.c',
+  'src/core/surface/completion_queue.c',
+  'src/core/surface/event_string.c',
+  'src/core/surface/init.c',
+  'src/core/surface/lame_client.c',
+  'src/core/surface/metadata_array.c',
+  'src/core/surface/server.c',
+  'src/core/surface/server_chttp2.c',
+  'src/core/surface/server_create.c',
+  'src/core/surface/validate_metadata.c',
+  'src/core/surface/version.c',
+  'src/core/transport/byte_stream.c',
+  'src/core/transport/chttp2/alpn.c',
+  'src/core/transport/chttp2/bin_encoder.c',
+  'src/core/transport/chttp2/frame_data.c',
+  'src/core/transport/chttp2/frame_goaway.c',
+  'src/core/transport/chttp2/frame_ping.c',
+  'src/core/transport/chttp2/frame_rst_stream.c',
+  'src/core/transport/chttp2/frame_settings.c',
+  'src/core/transport/chttp2/frame_window_update.c',
+  'src/core/transport/chttp2/hpack_encoder.c',
+  'src/core/transport/chttp2/hpack_parser.c',
+  'src/core/transport/chttp2/hpack_table.c',
+  'src/core/transport/chttp2/huffsyms.c',
+  'src/core/transport/chttp2/incoming_metadata.c',
+  'src/core/transport/chttp2/parsing.c',
+  'src/core/transport/chttp2/status_conversion.c',
+  'src/core/transport/chttp2/stream_lists.c',
+  'src/core/transport/chttp2/stream_map.c',
+  'src/core/transport/chttp2/timeout_encoding.c',
+  'src/core/transport/chttp2/varint.c',
+  'src/core/transport/chttp2/writing.c',
+  'src/core/transport/chttp2_transport.c',
+  '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',
+  'src/core/census/initialize.c',
+  'src/core/census/operation.c',
+  'src/core/census/tracing.c',
+  'src/boringssl/err_data.c',
+  'third_party/boringssl/crypto/aes/aes.c',
+  'third_party/boringssl/crypto/aes/mode_wrappers.c',
+  'third_party/boringssl/crypto/asn1/a_bitstr.c',
+  'third_party/boringssl/crypto/asn1/a_bool.c',
+  'third_party/boringssl/crypto/asn1/a_bytes.c',
+  'third_party/boringssl/crypto/asn1/a_d2i_fp.c',
+  'third_party/boringssl/crypto/asn1/a_dup.c',
+  'third_party/boringssl/crypto/asn1/a_enum.c',
+  'third_party/boringssl/crypto/asn1/a_gentm.c',
+  'third_party/boringssl/crypto/asn1/a_i2d_fp.c',
+  'third_party/boringssl/crypto/asn1/a_int.c',
+  'third_party/boringssl/crypto/asn1/a_mbstr.c',
+  'third_party/boringssl/crypto/asn1/a_object.c',
+  'third_party/boringssl/crypto/asn1/a_octet.c',
+  'third_party/boringssl/crypto/asn1/a_print.c',
+  'third_party/boringssl/crypto/asn1/a_strnid.c',
+  'third_party/boringssl/crypto/asn1/a_time.c',
+  'third_party/boringssl/crypto/asn1/a_type.c',
+  'third_party/boringssl/crypto/asn1/a_utctm.c',
+  'third_party/boringssl/crypto/asn1/a_utf8.c',
+  'third_party/boringssl/crypto/asn1/asn1_lib.c',
+  'third_party/boringssl/crypto/asn1/asn1_par.c',
+  'third_party/boringssl/crypto/asn1/asn_pack.c',
+  'third_party/boringssl/crypto/asn1/bio_asn1.c',
+  'third_party/boringssl/crypto/asn1/bio_ndef.c',
+  'third_party/boringssl/crypto/asn1/f_enum.c',
+  'third_party/boringssl/crypto/asn1/f_int.c',
+  'third_party/boringssl/crypto/asn1/f_string.c',
+  'third_party/boringssl/crypto/asn1/t_bitst.c',
+  'third_party/boringssl/crypto/asn1/t_pkey.c',
+  'third_party/boringssl/crypto/asn1/tasn_dec.c',
+  'third_party/boringssl/crypto/asn1/tasn_enc.c',
+  'third_party/boringssl/crypto/asn1/tasn_fre.c',
+  'third_party/boringssl/crypto/asn1/tasn_new.c',
+  'third_party/boringssl/crypto/asn1/tasn_prn.c',
+  'third_party/boringssl/crypto/asn1/tasn_typ.c',
+  'third_party/boringssl/crypto/asn1/tasn_utl.c',
+  'third_party/boringssl/crypto/asn1/x_bignum.c',
+  'third_party/boringssl/crypto/asn1/x_long.c',
+  'third_party/boringssl/crypto/base64/base64.c',
+  'third_party/boringssl/crypto/bio/bio.c',
+  'third_party/boringssl/crypto/bio/bio_mem.c',
+  'third_party/boringssl/crypto/bio/buffer.c',
+  'third_party/boringssl/crypto/bio/connect.c',
+  'third_party/boringssl/crypto/bio/fd.c',
+  'third_party/boringssl/crypto/bio/file.c',
+  'third_party/boringssl/crypto/bio/hexdump.c',
+  'third_party/boringssl/crypto/bio/pair.c',
+  'third_party/boringssl/crypto/bio/printf.c',
+  'third_party/boringssl/crypto/bio/socket.c',
+  'third_party/boringssl/crypto/bio/socket_helper.c',
+  'third_party/boringssl/crypto/bn/add.c',
+  'third_party/boringssl/crypto/bn/asm/x86_64-gcc.c',
+  'third_party/boringssl/crypto/bn/bn.c',
+  'third_party/boringssl/crypto/bn/bn_asn1.c',
+  'third_party/boringssl/crypto/bn/cmp.c',
+  'third_party/boringssl/crypto/bn/convert.c',
+  'third_party/boringssl/crypto/bn/ctx.c',
+  'third_party/boringssl/crypto/bn/div.c',
+  'third_party/boringssl/crypto/bn/exponentiation.c',
+  'third_party/boringssl/crypto/bn/gcd.c',
+  'third_party/boringssl/crypto/bn/generic.c',
+  'third_party/boringssl/crypto/bn/kronecker.c',
+  'third_party/boringssl/crypto/bn/montgomery.c',
+  'third_party/boringssl/crypto/bn/mul.c',
+  'third_party/boringssl/crypto/bn/prime.c',
+  'third_party/boringssl/crypto/bn/random.c',
+  'third_party/boringssl/crypto/bn/rsaz_exp.c',
+  'third_party/boringssl/crypto/bn/shift.c',
+  'third_party/boringssl/crypto/bn/sqrt.c',
+  'third_party/boringssl/crypto/buf/buf.c',
+  'third_party/boringssl/crypto/bytestring/ber.c',
+  'third_party/boringssl/crypto/bytestring/cbb.c',
+  'third_party/boringssl/crypto/bytestring/cbs.c',
+  'third_party/boringssl/crypto/chacha/chacha_generic.c',
+  'third_party/boringssl/crypto/chacha/chacha_vec.c',
+  'third_party/boringssl/crypto/cipher/aead.c',
+  'third_party/boringssl/crypto/cipher/cipher.c',
+  'third_party/boringssl/crypto/cipher/derive_key.c',
+  'third_party/boringssl/crypto/cipher/e_aes.c',
+  'third_party/boringssl/crypto/cipher/e_chacha20poly1305.c',
+  'third_party/boringssl/crypto/cipher/e_des.c',
+  'third_party/boringssl/crypto/cipher/e_null.c',
+  'third_party/boringssl/crypto/cipher/e_rc2.c',
+  'third_party/boringssl/crypto/cipher/e_rc4.c',
+  'third_party/boringssl/crypto/cipher/e_ssl3.c',
+  'third_party/boringssl/crypto/cipher/e_tls.c',
+  'third_party/boringssl/crypto/cipher/tls_cbc.c',
+  'third_party/boringssl/crypto/cmac/cmac.c',
+  'third_party/boringssl/crypto/conf/conf.c',
+  'third_party/boringssl/crypto/cpu-arm.c',
+  'third_party/boringssl/crypto/cpu-intel.c',
+  'third_party/boringssl/crypto/crypto.c',
+  'third_party/boringssl/crypto/curve25519/curve25519.c',
+  'third_party/boringssl/crypto/des/des.c',
+  'third_party/boringssl/crypto/dh/check.c',
+  'third_party/boringssl/crypto/dh/dh.c',
+  'third_party/boringssl/crypto/dh/dh_asn1.c',
+  'third_party/boringssl/crypto/dh/params.c',
+  'third_party/boringssl/crypto/digest/digest.c',
+  'third_party/boringssl/crypto/digest/digests.c',
+  'third_party/boringssl/crypto/directory_posix.c',
+  'third_party/boringssl/crypto/directory_win.c',
+  'third_party/boringssl/crypto/dsa/dsa.c',
+  'third_party/boringssl/crypto/dsa/dsa_asn1.c',
+  'third_party/boringssl/crypto/ec/ec.c',
+  'third_party/boringssl/crypto/ec/ec_asn1.c',
+  'third_party/boringssl/crypto/ec/ec_key.c',
+  'third_party/boringssl/crypto/ec/ec_montgomery.c',
+  'third_party/boringssl/crypto/ec/oct.c',
+  'third_party/boringssl/crypto/ec/p224-64.c',
+  'third_party/boringssl/crypto/ec/p256-64.c',
+  'third_party/boringssl/crypto/ec/p256-x86_64.c',
+  'third_party/boringssl/crypto/ec/simple.c',
+  'third_party/boringssl/crypto/ec/util-64.c',
+  'third_party/boringssl/crypto/ec/wnaf.c',
+  'third_party/boringssl/crypto/ecdh/ecdh.c',
+  'third_party/boringssl/crypto/ecdsa/ecdsa.c',
+  'third_party/boringssl/crypto/ecdsa/ecdsa_asn1.c',
+  'third_party/boringssl/crypto/engine/engine.c',
+  'third_party/boringssl/crypto/err/err.c',
+  'third_party/boringssl/crypto/evp/algorithm.c',
+  'third_party/boringssl/crypto/evp/digestsign.c',
+  'third_party/boringssl/crypto/evp/evp.c',
+  'third_party/boringssl/crypto/evp/evp_asn1.c',
+  'third_party/boringssl/crypto/evp/evp_ctx.c',
+  'third_party/boringssl/crypto/evp/p_dsa_asn1.c',
+  'third_party/boringssl/crypto/evp/p_ec.c',
+  'third_party/boringssl/crypto/evp/p_ec_asn1.c',
+  'third_party/boringssl/crypto/evp/p_rsa.c',
+  'third_party/boringssl/crypto/evp/p_rsa_asn1.c',
+  'third_party/boringssl/crypto/evp/pbkdf.c',
+  'third_party/boringssl/crypto/evp/sign.c',
+  'third_party/boringssl/crypto/ex_data.c',
+  'third_party/boringssl/crypto/hkdf/hkdf.c',
+  'third_party/boringssl/crypto/hmac/hmac.c',
+  'third_party/boringssl/crypto/lhash/lhash.c',
+  'third_party/boringssl/crypto/md4/md4.c',
+  'third_party/boringssl/crypto/md5/md5.c',
+  'third_party/boringssl/crypto/mem.c',
+  'third_party/boringssl/crypto/modes/cbc.c',
+  'third_party/boringssl/crypto/modes/cfb.c',
+  'third_party/boringssl/crypto/modes/ctr.c',
+  'third_party/boringssl/crypto/modes/gcm.c',
+  'third_party/boringssl/crypto/modes/ofb.c',
+  'third_party/boringssl/crypto/obj/obj.c',
+  'third_party/boringssl/crypto/obj/obj_xref.c',
+  'third_party/boringssl/crypto/pem/pem_all.c',
+  'third_party/boringssl/crypto/pem/pem_info.c',
+  'third_party/boringssl/crypto/pem/pem_lib.c',
+  'third_party/boringssl/crypto/pem/pem_oth.c',
+  'third_party/boringssl/crypto/pem/pem_pk8.c',
+  'third_party/boringssl/crypto/pem/pem_pkey.c',
+  'third_party/boringssl/crypto/pem/pem_x509.c',
+  'third_party/boringssl/crypto/pem/pem_xaux.c',
+  'third_party/boringssl/crypto/pkcs8/p5_pbe.c',
+  'third_party/boringssl/crypto/pkcs8/p5_pbev2.c',
+  'third_party/boringssl/crypto/pkcs8/p8_pkey.c',
+  'third_party/boringssl/crypto/pkcs8/pkcs8.c',
+  'third_party/boringssl/crypto/poly1305/poly1305.c',
+  'third_party/boringssl/crypto/poly1305/poly1305_arm.c',
+  'third_party/boringssl/crypto/poly1305/poly1305_vec.c',
+  'third_party/boringssl/crypto/rand/rand.c',
+  'third_party/boringssl/crypto/rand/urandom.c',
+  'third_party/boringssl/crypto/rand/windows.c',
+  'third_party/boringssl/crypto/rc4/rc4.c',
+  'third_party/boringssl/crypto/refcount_c11.c',
+  'third_party/boringssl/crypto/refcount_lock.c',
+  'third_party/boringssl/crypto/rsa/blinding.c',
+  'third_party/boringssl/crypto/rsa/padding.c',
+  'third_party/boringssl/crypto/rsa/rsa.c',
+  'third_party/boringssl/crypto/rsa/rsa_asn1.c',
+  'third_party/boringssl/crypto/rsa/rsa_impl.c',
+  'third_party/boringssl/crypto/sha/sha1.c',
+  'third_party/boringssl/crypto/sha/sha256.c',
+  'third_party/boringssl/crypto/sha/sha512.c',
+  'third_party/boringssl/crypto/stack/stack.c',
+  'third_party/boringssl/crypto/thread.c',
+  'third_party/boringssl/crypto/thread_none.c',
+  'third_party/boringssl/crypto/thread_pthread.c',
+  'third_party/boringssl/crypto/thread_win.c',
+  'third_party/boringssl/crypto/time_support.c',
+  'third_party/boringssl/crypto/x509/a_digest.c',
+  'third_party/boringssl/crypto/x509/a_sign.c',
+  'third_party/boringssl/crypto/x509/a_strex.c',
+  'third_party/boringssl/crypto/x509/a_verify.c',
+  'third_party/boringssl/crypto/x509/asn1_gen.c',
+  'third_party/boringssl/crypto/x509/by_dir.c',
+  'third_party/boringssl/crypto/x509/by_file.c',
+  'third_party/boringssl/crypto/x509/i2d_pr.c',
+  'third_party/boringssl/crypto/x509/pkcs7.c',
+  'third_party/boringssl/crypto/x509/t_crl.c',
+  'third_party/boringssl/crypto/x509/t_req.c',
+  'third_party/boringssl/crypto/x509/t_x509.c',
+  'third_party/boringssl/crypto/x509/t_x509a.c',
+  'third_party/boringssl/crypto/x509/x509.c',
+  'third_party/boringssl/crypto/x509/x509_att.c',
+  'third_party/boringssl/crypto/x509/x509_cmp.c',
+  'third_party/boringssl/crypto/x509/x509_d2.c',
+  'third_party/boringssl/crypto/x509/x509_def.c',
+  'third_party/boringssl/crypto/x509/x509_ext.c',
+  'third_party/boringssl/crypto/x509/x509_lu.c',
+  'third_party/boringssl/crypto/x509/x509_obj.c',
+  'third_party/boringssl/crypto/x509/x509_r2x.c',
+  'third_party/boringssl/crypto/x509/x509_req.c',
+  'third_party/boringssl/crypto/x509/x509_set.c',
+  'third_party/boringssl/crypto/x509/x509_trs.c',
+  'third_party/boringssl/crypto/x509/x509_txt.c',
+  'third_party/boringssl/crypto/x509/x509_v3.c',
+  'third_party/boringssl/crypto/x509/x509_vfy.c',
+  'third_party/boringssl/crypto/x509/x509_vpm.c',
+  'third_party/boringssl/crypto/x509/x509cset.c',
+  'third_party/boringssl/crypto/x509/x509name.c',
+  'third_party/boringssl/crypto/x509/x509rset.c',
+  'third_party/boringssl/crypto/x509/x509spki.c',
+  'third_party/boringssl/crypto/x509/x509type.c',
+  'third_party/boringssl/crypto/x509/x_algor.c',
+  'third_party/boringssl/crypto/x509/x_all.c',
+  'third_party/boringssl/crypto/x509/x_attrib.c',
+  'third_party/boringssl/crypto/x509/x_crl.c',
+  'third_party/boringssl/crypto/x509/x_exten.c',
+  'third_party/boringssl/crypto/x509/x_info.c',
+  'third_party/boringssl/crypto/x509/x_name.c',
+  'third_party/boringssl/crypto/x509/x_pkey.c',
+  'third_party/boringssl/crypto/x509/x_pubkey.c',
+  'third_party/boringssl/crypto/x509/x_req.c',
+  'third_party/boringssl/crypto/x509/x_sig.c',
+  'third_party/boringssl/crypto/x509/x_spki.c',
+  'third_party/boringssl/crypto/x509/x_val.c',
+  'third_party/boringssl/crypto/x509/x_x509.c',
+  'third_party/boringssl/crypto/x509/x_x509a.c',
+  'third_party/boringssl/crypto/x509v3/pcy_cache.c',
+  'third_party/boringssl/crypto/x509v3/pcy_data.c',
+  'third_party/boringssl/crypto/x509v3/pcy_lib.c',
+  'third_party/boringssl/crypto/x509v3/pcy_map.c',
+  'third_party/boringssl/crypto/x509v3/pcy_node.c',
+  'third_party/boringssl/crypto/x509v3/pcy_tree.c',
+  'third_party/boringssl/crypto/x509v3/v3_akey.c',
+  'third_party/boringssl/crypto/x509v3/v3_akeya.c',
+  'third_party/boringssl/crypto/x509v3/v3_alt.c',
+  'third_party/boringssl/crypto/x509v3/v3_bcons.c',
+  'third_party/boringssl/crypto/x509v3/v3_bitst.c',
+  'third_party/boringssl/crypto/x509v3/v3_conf.c',
+  'third_party/boringssl/crypto/x509v3/v3_cpols.c',
+  'third_party/boringssl/crypto/x509v3/v3_crld.c',
+  'third_party/boringssl/crypto/x509v3/v3_enum.c',
+  'third_party/boringssl/crypto/x509v3/v3_extku.c',
+  'third_party/boringssl/crypto/x509v3/v3_genn.c',
+  'third_party/boringssl/crypto/x509v3/v3_ia5.c',
+  'third_party/boringssl/crypto/x509v3/v3_info.c',
+  'third_party/boringssl/crypto/x509v3/v3_int.c',
+  'third_party/boringssl/crypto/x509v3/v3_lib.c',
+  'third_party/boringssl/crypto/x509v3/v3_ncons.c',
+  'third_party/boringssl/crypto/x509v3/v3_pci.c',
+  'third_party/boringssl/crypto/x509v3/v3_pcia.c',
+  'third_party/boringssl/crypto/x509v3/v3_pcons.c',
+  'third_party/boringssl/crypto/x509v3/v3_pku.c',
+  'third_party/boringssl/crypto/x509v3/v3_pmaps.c',
+  'third_party/boringssl/crypto/x509v3/v3_prn.c',
+  'third_party/boringssl/crypto/x509v3/v3_purp.c',
+  'third_party/boringssl/crypto/x509v3/v3_skey.c',
+  'third_party/boringssl/crypto/x509v3/v3_sxnet.c',
+  'third_party/boringssl/crypto/x509v3/v3_utl.c',
+  'third_party/boringssl/ssl/custom_extensions.c',
+  'third_party/boringssl/ssl/d1_both.c',
+  'third_party/boringssl/ssl/d1_clnt.c',
+  'third_party/boringssl/ssl/d1_lib.c',
+  'third_party/boringssl/ssl/d1_meth.c',
+  'third_party/boringssl/ssl/d1_pkt.c',
+  'third_party/boringssl/ssl/d1_srtp.c',
+  'third_party/boringssl/ssl/d1_srvr.c',
+  'third_party/boringssl/ssl/dtls_record.c',
+  'third_party/boringssl/ssl/pqueue/pqueue.c',
+  'third_party/boringssl/ssl/s3_both.c',
+  'third_party/boringssl/ssl/s3_clnt.c',
+  'third_party/boringssl/ssl/s3_enc.c',
+  'third_party/boringssl/ssl/s3_lib.c',
+  'third_party/boringssl/ssl/s3_meth.c',
+  'third_party/boringssl/ssl/s3_pkt.c',
+  'third_party/boringssl/ssl/s3_srvr.c',
+  'third_party/boringssl/ssl/ssl_aead_ctx.c',
+  'third_party/boringssl/ssl/ssl_asn1.c',
+  'third_party/boringssl/ssl/ssl_buffer.c',
+  'third_party/boringssl/ssl/ssl_cert.c',
+  'third_party/boringssl/ssl/ssl_cipher.c',
+  'third_party/boringssl/ssl/ssl_file.c',
+  'third_party/boringssl/ssl/ssl_lib.c',
+  'third_party/boringssl/ssl/ssl_rsa.c',
+  'third_party/boringssl/ssl/ssl_session.c',
+  'third_party/boringssl/ssl/ssl_stat.c',
+  'third_party/boringssl/ssl/t1_enc.c',
+  'third_party/boringssl/ssl/t1_lib.c',
+  'third_party/boringssl/ssl/tls_record.c',
+]
diff --git a/src/python/grpcio/tests/unit/_cython/test_utilities.py b/src/python/grpcio/tests/unit/_cython/test_utilities.py
index 21ea307..6a09739 100644
--- a/src/python/grpcio/tests/unit/_cython/test_utilities.py
+++ b/src/python/grpcio/tests/unit/_cython/test_utilities.py
@@ -29,7 +29,7 @@
 
 import threading
 
-from grpc._cython._cygrpc import completion_queue
+from grpc._cython import cygrpc
 
 
 class CompletionQueuePollFuture:
diff --git a/src/python/grpcio/tox.ini b/src/python/grpcio/tox.ini
deleted file mode 100644
index bfb1ca0..0000000
--- a/src/python/grpcio/tox.ini
+++ /dev/null
@@ -1,19 +0,0 @@
-# Tox (http://tox.testrun.org/) is a tool for running tests
-# in multiple virtualenvs. This configuration file will run the
-# test suite on all supported python versions. To use it, "pip install tox"
-# and then run "tox" from this directory.
-
-[tox]
-skipsdist = true
-envlist = py27
-
-[testenv]
-commands =
-    {envpython} setup.py build_py
-    {envpython} setup.py test
-    coverage combine
-    coverage html --include='grpc/*' --omit='grpc/framework/alpha/*','grpc/early_adopter/*','grpc/framework/base/*','grpc/framework/face/*','grpc/_adapter/fore.py','grpc/_adapter/rear.py'
-    coverage report --include='grpc/*' --omit='grpc/framework/alpha/*','grpc/early_adopter/*','grpc/framework/base/*','grpc/framework/face/*','grpc/_adapter/fore.py','grpc/_adapter/rear.py'
-deps =
-    -rrequirements.txt
-passenv = *
diff --git a/src/ruby/.rspec b/src/ruby/.rspec
deleted file mode 100755
index efeee2c..0000000
--- a/src/ruby/.rspec
+++ /dev/null
@@ -1,6 +0,0 @@
--I.
--Ipb
---backtrace
---require spec_helper
---format documentation
---color
diff --git a/src/ruby/ext/grpc/extconf.rb b/src/ruby/ext/grpc/extconf.rb
index db9385e..018353c 100644
--- a/src/ruby/ext/grpc/extconf.rb
+++ b/src/ruby/ext/grpc/extconf.rb
@@ -54,53 +54,30 @@
   LIBDIR
 ]
 
-def check_grpc_root
-  grpc_root = ENV['GRPC_ROOT']
-  if grpc_root.nil?
-    r = File.expand_path(File.join(File.dirname(__FILE__), '../../../..'))
-    grpc_root = r if File.exist?(File.join(r, 'include/grpc/grpc.h'))
-  end
-  grpc_root
+fail 'libdl not found' unless have_library('dl', 'dlopen')
+fail 'zlib not found' unless have_library('z', 'inflate')
+
+grpc_root = File.expand_path(File.join(File.dirname(__FILE__), '../../../..'))
+
+grpc_config = ENV['GRPC_CONFIG'] || 'opt'
+
+if ENV.key?('GRPC_LIB_DIR')
+  grpc_lib_dir = File.join(grpc_root, ENV['GRPC_LIB_DIR'])
+else
+  grpc_lib_dir = File.join(File.join(grpc_root, 'libs'), grpc_config)
 end
 
-grpc_pkg_config = system('pkg-config --exists grpc')
+unless File.exist?(File.join(grpc_lib_dir, 'libgrpc.a'))
+  print "Building internal gRPC\n"
+  system("make -C #{grpc_root} static_c CONFIG=#{grpc_config}")
+end
 
-if grpc_pkg_config
-  $CFLAGS << ' ' + `pkg-config --static --cflags grpc`.strip + ' '
-  $LDFLAGS << ' ' + `pkg-config --static --libs grpc`.strip + ' '
-else
-  dir_config('grpc', HEADER_DIRS, LIB_DIRS)
-  fail 'libdl not found' unless have_library('dl', 'dlopen')
-  fail 'zlib not found' unless have_library('z', 'inflate')
-  begin
-    fail 'Fail' unless have_library('gpr', 'gpr_now')
-    fail 'Fail' unless have_library('grpc', 'grpc_channel_destroy')
-  rescue
-    # Check to see if GRPC_ROOT is defined or available
-    grpc_root = check_grpc_root
-
-    # Stop if there is still no grpc_root
-    exit 1 if grpc_root.nil?
-
-    grpc_config = ENV['GRPC_CONFIG'] || 'opt'
-    if ENV.key?('GRPC_LIB_DIR')
-      grpc_lib_dir = File.join(grpc_root, ENV['GRPC_LIB_DIR'])
-    else
-      grpc_lib_dir = File.join(File.join(grpc_root, 'libs'), grpc_config)
-    end
-    unless File.exist?(File.join(grpc_lib_dir, 'libgrpc.a'))
-      print "Building internal gRPC\n"
-      system("make -C #{grpc_root} static_c CONFIG=#{grpc_config}")
-    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
+$CFLAGS << ' -I' + File.join(grpc_root, 'include')
+$LDFLAGS << ' ' + File.join(grpc_lib_dir, 'libgrpc.a')
+$LDFLAGS << ' ' + File.join(grpc_lib_dir, 'libgpr.a')
+if grpc_config == 'gcov'
+  $CFLAGS << ' -O0 -fprofile-arcs -ftest-coverage'
+  $LDFLAGS << ' -fprofile-arcs -ftest-coverage -rdynamic'
 end
 
 $CFLAGS << ' -std=c99 '
@@ -109,4 +86,7 @@
 $CFLAGS << ' -pedantic '
 $CFLAGS << ' -Werror '
 
+$LDFLAGS << ' -lssl '
+$LDFLAGS << ' -lcrypto '
+
 create_makefile('grpc/grpc')
diff --git a/src/ruby/ext/grpc/rb_call.c b/src/ruby/ext/grpc/rb_call.c
index 1647d9b..43adafb 100644
--- a/src/ruby/ext/grpc/rb_call.c
+++ b/src/ruby/ext/grpc/rb_call.c
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -310,33 +310,61 @@
   grpc_metadata_array *md_ary = NULL;
   long array_length;
   long i;
+  char *key_str;
+  size_t key_len;
+  char *value_str;
+  size_t value_len;
+
+  if (TYPE(key) == T_SYMBOL) {
+    key_str = (char *)rb_id2name(SYM2ID(key));
+    key_len = strlen(key_str);
+  } else { /* StringValueCStr does all other type exclusions for us */
+    key_str = StringValueCStr(key);
+    key_len = RSTRING_LEN(key);
+  }
+
+  if (!grpc_header_key_is_legal(key_str, key_len)) {
+    rb_raise(rb_eArgError,
+             "'%s' is an invalid header key, must match [a-z0-9-_.]+",
+             key_str);
+    return ST_STOP;
+  }
 
   /* Construct a metadata object from key and value and add it */
   TypedData_Get_Struct(md_ary_obj, grpc_metadata_array,
                        &grpc_rb_md_ary_data_type, md_ary);
 
   if (TYPE(val) == T_ARRAY) {
-    /* If the value is an array, add capacity for each value in the array */
     array_length = RARRAY_LEN(val);
+    /* If the value is an array, add capacity for each value in the array */
     for (i = 0; i < array_length; i++) {
-      if (TYPE(key) == T_SYMBOL) {
-        md_ary->metadata[md_ary->count].key = (char *)rb_id2name(SYM2ID(key));
-      } else { /* StringValueCStr does all other type exclusions for us */
-        md_ary->metadata[md_ary->count].key = StringValueCStr(key);
+      value_str = RSTRING_PTR(rb_ary_entry(val, i));
+      value_len = RSTRING_LEN(rb_ary_entry(val, i));
+      if (!grpc_is_binary_header(key_str, key_len) &&
+          !grpc_header_nonbin_value_is_legal(value_str, value_len)) {
+        // The value has invalid characters
+        rb_raise(rb_eArgError,
+                 "Header value '%s' has invalid characters", value_str);
+        return ST_STOP;
       }
-      md_ary->metadata[md_ary->count].value = RSTRING_PTR(rb_ary_entry(val, i));
-      md_ary->metadata[md_ary->count].value_length =
-          RSTRING_LEN(rb_ary_entry(val, i));
+      md_ary->metadata[md_ary->count].key = key_str;
+      md_ary->metadata[md_ary->count].value = value_str;
+      md_ary->metadata[md_ary->count].value_length = value_len;
       md_ary->count += 1;
     }
   } else {
-    if (TYPE(key) == T_SYMBOL) {
-      md_ary->metadata[md_ary->count].key = (char *)rb_id2name(SYM2ID(key));
-    } else { /* StringValueCStr does all other type exclusions for us */
-      md_ary->metadata[md_ary->count].key = StringValueCStr(key);
+    value_str = RSTRING_PTR(val);
+    value_len = RSTRING_LEN(val);
+    if (!grpc_is_binary_header(key_str, key_len) &&
+        !grpc_header_nonbin_value_is_legal(value_str, value_len)) {
+      // The value has invalid characters
+      rb_raise(rb_eArgError,
+               "Header value '%s' has invalid characters", value_str);
+      return ST_STOP;
     }
-    md_ary->metadata[md_ary->count].value = RSTRING_PTR(val);
-    md_ary->metadata[md_ary->count].value_length = RSTRING_LEN(val);
+    md_ary->metadata[md_ary->count].key = key_str;
+    md_ary->metadata[md_ary->count].value = value_str;
+    md_ary->metadata[md_ary->count].value_length = value_len;
     md_ary->count += 1;
   }
 
diff --git a/src/ruby/ext/grpc/rb_call_credentials.c b/src/ruby/ext/grpc/rb_call_credentials.c
index acc5472..4d719d7 100644
--- a/src/ruby/ext/grpc/rb_call_credentials.c
+++ b/src/ruby/ext/grpc/rb_call_credentials.c
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -38,8 +38,10 @@
 
 #include <grpc/grpc.h>
 #include <grpc/grpc_security.h>
+#include <grpc/support/alloc.h>
 
 #include "rb_call.h"
+#include "rb_event_thread.h"
 #include "rb_grpc.h"
 
 /* grpc_rb_cCallCredentials is the ruby class that proxies
@@ -87,7 +89,7 @@
   return result;
 }
 
-static void *grpc_rb_call_credentials_callback_with_gil(void *param) {
+static void grpc_rb_call_credentials_callback_with_gil(void *param) {
   callback_params *const params = (callback_params *)param;
   VALUE auth_uri = rb_str_new_cstr(params->context.service_url);
   /* Pass the arguments to the proc in a hash, which currently only has they key
@@ -113,21 +115,20 @@
   params->callback(params->user_data, md_ary.metadata, md_ary.count, status,
                    error_details);
   grpc_metadata_array_destroy(&md_ary);
-
-  return NULL;
+  gpr_free(params);
 }
 
 static void grpc_rb_call_credentials_plugin_get_metadata(
     void *state, grpc_auth_metadata_context context,
     grpc_credentials_plugin_metadata_cb cb, void *user_data) {
-  callback_params params;
-  params.get_metadata = (VALUE)state;
-  params.context = context;
-  params.user_data = user_data;
-  params.callback = cb;
+  callback_params *params = gpr_malloc(sizeof(callback_params));
+  params->get_metadata = (VALUE)state;
+  params->context = context;
+  params->user_data = user_data;
+  params->callback = cb;
 
-  rb_thread_call_with_gvl(grpc_rb_call_credentials_callback_with_gil,
-                          (void*)(&params));
+  grpc_rb_event_queue_enqueue(grpc_rb_call_credentials_callback_with_gil,
+                              (void*)(params));
 }
 
 static void grpc_rb_call_credentials_plugin_destroy(void *state) {
@@ -300,6 +301,8 @@
                    grpc_rb_call_credentials_compose, -1);
 
   id_callback = rb_intern("__callback");
+
+  grpc_rb_event_queue_thread_start();
 }
 
 /* Gets the wrapped grpc_call_credentials from the ruby wrapper */
diff --git a/src/ruby/ext/grpc/rb_channel.c b/src/ruby/ext/grpc/rb_channel.c
index d5d8242..2fb8a5c 100644
--- a/src/ruby/ext/grpc/rb_channel.c
+++ b/src/ruby/ext/grpc/rb_channel.c
@@ -59,6 +59,9 @@
  * GCed before the channel */
 static ID id_cqueue;
 
+/* id_insecure_channel is used to indicate that a channel is insecure */
+static VALUE id_insecure_channel;
+
 /* grpc_rb_cChannel is the ruby class that proxies grpc_channel. */
 static VALUE grpc_rb_cChannel = Qnil;
 
@@ -126,7 +129,8 @@
 
 /*
   call-seq:
-    insecure_channel = Channel:new("myhost:8080", {'arg1': 'value1'})
+    insecure_channel = Channel:new("myhost:8080", {'arg1': 'value1'},
+                                   :this_channel_is_insecure)
     creds = ...
     secure_channel = Channel:new("myhost:443", {'arg1': 'value1'}, creds)
 
@@ -142,13 +146,18 @@
   grpc_channel_args args;
   MEMZERO(&args, grpc_channel_args, 1);
 
-  /* "21" == 2 mandatory args, 1 (credentials) is optional */
-  rb_scan_args(argc, argv, "21", &target, &channel_args, &credentials);
+  /* "3" == 3 mandatory args */
+  rb_scan_args(argc, argv, "3", &target, &channel_args, &credentials);
 
   TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper);
   target_chars = StringValueCStr(target);
   grpc_rb_hash_convert_to_channel_args(channel_args, &args);
-  if (credentials == Qnil) {
+  if (TYPE(credentials) == T_SYMBOL) {
+    if (id_insecure_channel != SYM2ID(credentials)) {
+      rb_raise(rb_eTypeError,
+               "bad creds symbol, want :this_channel_is_insecure");
+      return Qnil;
+    }
     ch = grpc_insecure_channel_create(target_chars, &args, NULL);
   } else {
     creds = grpc_rb_get_wrapped_channel_credentials(credentials);
@@ -408,6 +417,7 @@
                   ID2SYM(rb_intern(GRPC_ARG_MAX_CONCURRENT_STREAMS)));
   rb_define_const(grpc_rb_cChannel, "MAX_MESSAGE_LENGTH",
                   ID2SYM(rb_intern(GRPC_ARG_MAX_MESSAGE_LENGTH)));
+  id_insecure_channel = rb_intern("this_channel_is_insecure");
   Init_grpc_propagate_masks();
   Init_grpc_connectivity_states();
 }
diff --git a/src/ruby/ext/grpc/rb_event_thread.c b/src/ruby/ext/grpc/rb_event_thread.c
new file mode 100644
index 0000000..95af091
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_event_thread.c
@@ -0,0 +1,153 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "rb_event_thread.h"
+
+#include <stdbool.h>
+
+#include <ruby/ruby.h>
+#include <ruby/thread.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+#include <grpc/support/log.h>
+
+typedef struct grpc_rb_event {
+  // callback will be called with argument while holding the GVL
+  void (*callback)(void*);
+  void *argument;
+
+  struct grpc_rb_event *next;
+} grpc_rb_event;
+
+typedef struct grpc_rb_event_queue {
+  grpc_rb_event *head;
+  grpc_rb_event *tail;
+
+  gpr_mu mu;
+  gpr_cv cv;
+
+  // Indicates that the thread should stop waiting
+  bool abort;
+} grpc_rb_event_queue;
+
+static grpc_rb_event_queue event_queue;
+
+void grpc_rb_event_queue_enqueue(void (*callback)(void*),
+                                 void *argument) {
+  grpc_rb_event *event = gpr_malloc(sizeof(grpc_rb_event));
+  event->callback = callback;
+  event->argument = argument;
+  event->next = NULL;
+  gpr_mu_lock(&event_queue.mu);
+  if (event_queue.tail == NULL) {
+    event_queue.head = event_queue.tail = event;
+  } else {
+    event_queue.tail->next = event;
+    event_queue.tail = event;
+  }
+  gpr_cv_signal(&event_queue.cv);
+  gpr_mu_unlock(&event_queue.mu);
+}
+
+static grpc_rb_event *grpc_rb_event_queue_dequeue() {
+  grpc_rb_event *event;
+  if (event_queue.head == NULL) {
+    event = NULL;
+  } else {
+    event = event_queue.head;
+    if (event_queue.head->next == NULL) {
+      event_queue.head = event_queue.tail = NULL;
+    } else {
+      event_queue.head = event_queue.head->next;
+    }
+  }
+  return event;
+}
+
+static void grpc_rb_event_queue_destroy() {
+  gpr_mu_destroy(&event_queue.mu);
+  gpr_cv_destroy(&event_queue.cv);
+}
+
+static void *grpc_rb_wait_for_event_no_gil(void *param) {
+  grpc_rb_event *event = NULL;
+  gpr_mu_lock(&event_queue.mu);
+  while ((event = grpc_rb_event_queue_dequeue()) == NULL) {
+    gpr_cv_wait(&event_queue.cv,
+                &event_queue.mu,
+                gpr_inf_future(GPR_CLOCK_REALTIME));
+    if (event_queue.abort) {
+      gpr_mu_unlock(&event_queue.mu);
+      return NULL;
+    }
+  }
+  gpr_mu_unlock(&event_queue.mu);
+  return event;
+}
+
+static void grpc_rb_event_unblocking_func(void *arg) {
+  gpr_mu_lock(&event_queue.mu);
+  event_queue.abort = true;
+  gpr_cv_signal(&event_queue.cv);
+  gpr_mu_unlock(&event_queue.mu);
+}
+
+/* This is the implementation of the thread that handles auth metadata plugin
+ * events */
+static VALUE grpc_rb_event_thread(VALUE arg) {
+  grpc_rb_event *event;
+  while(true) {
+    event = (grpc_rb_event*)rb_thread_call_without_gvl(
+        grpc_rb_wait_for_event_no_gil, NULL,
+        grpc_rb_event_unblocking_func, NULL);
+    if (event == NULL) {
+      // Indicates that the thread needs to shut down
+      break;
+    } else {
+      event->callback(event->argument);
+      gpr_free(event);
+    }
+  }
+  grpc_rb_event_queue_destroy();
+  return Qnil;
+}
+
+void grpc_rb_event_queue_thread_start() {
+  event_queue.head = event_queue.tail = NULL;
+  event_queue.abort = false;
+  gpr_mu_init(&event_queue.mu);
+  gpr_cv_init(&event_queue.cv);
+
+  rb_thread_create(grpc_rb_event_thread, NULL);
+}
diff --git a/src/ruby/ext/grpc/rb_event_thread.h b/src/ruby/ext/grpc/rb_event_thread.h
new file mode 100644
index 0000000..46638bf
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_event_thread.h
@@ -0,0 +1,37 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+void grpc_rb_event_queue_thread_start();
+
+void grpc_rb_event_queue_enqueue(void (*callback)(void*),
+                                 void *argument);
diff --git a/src/ruby/grpc.gemspec b/src/ruby/grpc.gemspec
deleted file mode 100755
index 363abe9..0000000
--- a/src/ruby/grpc.gemspec
+++ /dev/null
@@ -1,45 +0,0 @@
-# -*- ruby -*-
-# encoding: utf-8
-$LOAD_PATH.push File.expand_path('../lib', __FILE__)
-require 'grpc/version'
-
-Gem::Specification.new do |s|
-  s.name          = 'grpc'
-  s.version       = GRPC::VERSION
-  s.authors       = ['gRPC Authors']
-  s.email         = 'temiola@google.com'
-  s.homepage      = 'https://github.com/google/grpc/tree/master/src/ruby'
-  s.summary       = 'GRPC system in Ruby'
-  s.description   = 'Send RPCs from Ruby using GRPC'
-  s.license       = 'BSD-3-Clause'
-
-  s.required_ruby_version = '>= 2.0.0'
-  s.requirements << 'libgrpc ~> 0.11.0 needs to be installed'
-
-  s.files = %w( Rakefile )
-  s.files += Dir.glob('bin/**/*')
-  s.files += Dir.glob('ext/**/*')
-  s.files += Dir.glob('lib/**/*')
-  s.files += Dir.glob('pb/**/*')
-  s.test_files = Dir.glob('spec/**/*')
-  %w(math noproto).each do |b|
-    s.executables += ["#{b}_client.rb", "#{b}_server.rb"]
-  end
-  s.executables += %w(grpc_ruby_interop_client grpc_ruby_interop_server)
-  s.require_paths = %w( bin lib pb )
-  s.platform      = Gem::Platform::RUBY
-
-  s.add_dependency 'google-protobuf', '~> 3.0.0alpha.1.1'
-  s.add_dependency 'googleauth', '~> 0.4'
-
-  s.add_development_dependency 'bundler', '~> 1.9'
-  s.add_development_dependency 'logging', '~> 2.0'
-  s.add_development_dependency 'simplecov', '~> 0.9'
-  s.add_development_dependency 'rake', '~> 10.4'
-  s.add_development_dependency 'rake-compiler', '~> 0.9'
-  s.add_development_dependency 'rspec', '~> 3.2'
-  s.add_development_dependency 'rubocop', '~> 0.30.0'
-  s.add_development_dependency 'signet', '~>0.6.0'
-
-  s.extensions = %w(ext/grpc/extconf.rb)
-end
diff --git a/src/ruby/lib/grpc.rb b/src/ruby/lib/grpc.rb
index 80b5743..1671ba3 100644
--- a/src/ruby/lib/grpc.rb
+++ b/src/ruby/lib/grpc.rb
@@ -1,4 +1,4 @@
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -27,6 +27,11 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+ssl_roots_path = File.expand_path('../../../../etc/roots.pem', __FILE__)
+unless ENV['GRPC_DEFAULT_SSL_ROOTS_FILE_PATH']
+  ENV['GRPC_DEFAULT_SSL_ROOTS_FILE_PATH'] = ssl_roots_path
+end
+
 require 'grpc/errors'
 require 'grpc/grpc'
 require 'grpc/logconfig'
diff --git a/src/ruby/lib/grpc/generic/client_stub.rb b/src/ruby/lib/grpc/generic/client_stub.rb
index 13100a6..4da9ff0 100644
--- a/src/ruby/lib/grpc/generic/client_stub.rb
+++ b/src/ruby/lib/grpc/generic/client_stub.rb
@@ -1,4 +1,4 @@
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -50,9 +50,8 @@
         return alt_chan
       end
       kw['grpc.primary_user_agent'] = "grpc-ruby/#{VERSION}"
-      return Core::Channel.new(host, kw) if creds.nil?
-      unless creds.is_a?(Core::ChannelCredentials)
-        fail(TypeError, '!ChannelCredentials')
+      unless creds.is_a?(Core::ChannelCredentials) || creds.is_a?(Symbol)
+        fail(TypeError, '!ChannelCredentials or Symbol')
       end
       Core::Channel.new(host, kw, creds)
     end
@@ -68,7 +67,8 @@
     # Minimally, a stub is created with the just the host of the gRPC service
     # it wishes to access, e.g.,
     #
-    # my_stub = ClientStub.new(example.host.com:50505)
+    # my_stub = ClientStub.new(example.host.com:50505,
+    #                          :this_channel_is_insecure)
     #
     # Any arbitrary keyword arguments are treated as channel arguments used to
     # configure the RPC connection to the host.
@@ -86,14 +86,14 @@
     #
     # @param host [String] the host the stub connects to
     # @param q [Core::CompletionQueue] used to wait for events
+    # @param creds [Core::ChannelCredentials|Symbol] the channel credentials, or
+    #     :this_channel_is_insecure
     # @param channel_override [Core::Channel] a pre-created channel
     # @param timeout [Number] the default timeout to use in requests
-    # @param creds [Core::ChannelCredentials] the channel credentials
     # @param kw [KeywordArgs]the channel arguments
-    def initialize(host, q,
+    def initialize(host, q, creds,
                    channel_override: nil,
                    timeout: nil,
-                   creds: nil,
                    propagate_mask: nil,
                    **kw)
       fail(TypeError, '!CompletionQueue') unless q.is_a?(Core::CompletionQueue)
@@ -464,7 +464,7 @@
                              method,
                              nil, # host use nil,
                              deadline)
-      call.set_credentials credentials unless credentials.nil?
+      call.set_credentials! credentials unless credentials.nil?
       ActiveCall.new(call, @queue, marshal, unmarshal, deadline, started: false)
     end
   end
diff --git a/src/ruby/lib/grpc/generic/service.rb b/src/ruby/lib/grpc/generic/service.rb
index 80ff669..410e1ad 100644
--- a/src/ruby/lib/grpc/generic/service.rb
+++ b/src/ruby/lib/grpc/generic/service.rb
@@ -160,10 +160,12 @@
         route_prefix = service_name
         Class.new(ClientStub) do
           # @param host [String] the host the stub connects to
+          # @param creds [Core::ChannelCredentials|Symbol] The channel
+          #     credentials to use, or :this_channel_is_insecure otherwise
           # @param kw [KeywordArgs] the channel arguments, plus any optional
           #                         args for configuring the client's channel
-          def initialize(host, **kw)
-            super(host, Core::CompletionQueue.new, **kw)
+          def initialize(host, creds, **kw)
+            super(host, Core::CompletionQueue.new, creds, **kw)
           end
 
           # Used define_method to add a method for each rpc_desc.  Each method
diff --git a/src/ruby/lib/grpc/version.rb b/src/ruby/lib/grpc/version.rb
index 9acf7a0..956e022 100644
--- a/src/ruby/lib/grpc/version.rb
+++ b/src/ruby/lib/grpc/version.rb
@@ -1,4 +1,4 @@
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -29,5 +29,5 @@
 
 # GRPC contains the General RPC module.
 module GRPC
-  VERSION = '0.11.1'
+  VERSION = '0.12.0'
 end
diff --git a/src/ruby/pb/test/client.rb b/src/ruby/pb/test/client.rb
index 6cc616e..684ee80 100755
--- a/src/ruby/pb/test/client.rb
+++ b/src/ruby/pb/test/client.rb
@@ -1,6 +1,6 @@
 #!/usr/bin/env ruby
 
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -56,8 +56,6 @@
 require 'test/proto/messages'
 require 'test/proto/test_services'
 
-require 'signet/ssl_config'
-
 AUTH_ENV = Google::Auth::CredentialsLoader::ENV_VAR
 
 # RubyLogger defines a logger for gRPC based on the standard ruby logger.
@@ -114,8 +112,8 @@
 def create_stub(opts)
   address = "#{opts.host}:#{opts.port}"
   if opts.secure
+    creds = ssl_creds(opts.use_test_ca)
     stub_opts = {
-      :creds => ssl_creds(opts.use_test_ca),
       GRPC::Core::Channel::SSL_TARGET => opts.host_override
     }
 
@@ -125,7 +123,7 @@
       unless opts.oauth_scope.nil?
         auth_creds = Google::Auth.get_application_default(opts.oauth_scope)
         call_creds = GRPC::Core::CallCredentials.new(auth_creds.updater_proc)
-        stub_opts[:creds] = stub_opts[:creds].compose call_creds
+        creds = creds.compose call_creds
       end
     end
 
@@ -135,20 +133,20 @@
 
       # use a metadata update proc that just adds the auth token.
       call_creds = GRPC::Core::CallCredentials.new(proc { |md| md.merge(kw) })
-      stub_opts[:creds] = stub_opts[:creds].compose call_creds
+      creds = creds.compose call_creds
     end
 
     if opts.test_case == 'jwt_token_creds'  # don't use a scope
       auth_creds = Google::Auth.get_application_default
       call_creds = GRPC::Core::CallCredentials.new(auth_creds.updater_proc)
-      stub_opts[:creds] = stub_opts[:creds].compose call_creds
+      creds = creds.compose call_creds
     end
 
     GRPC.logger.info("... connecting securely to #{address}")
-    Grpc::Testing::TestService::Stub.new(address, **stub_opts)
+    Grpc::Testing::TestService::Stub.new(address, creds, **stub_opts)
   else
     GRPC.logger.info("... connecting insecurely to #{address}")
-    Grpc::Testing::TestService::Stub.new(address)
+    Grpc::Testing::TestService::Stub.new(address, :this_channel_is_insecure)
   end
 end
 
@@ -266,16 +264,15 @@
 
   def per_rpc_creds
     auth_creds = Google::Auth.get_application_default(@args.oauth_scope)
-    kw = auth_creds.updater_proc.call({})
+    update_metadata = proc do |md|
+      kw = auth_creds.updater_proc.call({})
+    end
 
-    # TODO(jtattermusch): downcase the metadata keys here to make sure
-    # they are not rejected by C core. This is a hotfix that should
-    # be addressed by introducing auto-downcasing logic.
-    kw = Hash[ kw.each_pair.map { |k, v|  [k.downcase, v] }]
+    call_creds = GRPC::Core::CallCredentials.new(update_metadata)
 
     resp = perform_large_unary(fill_username: true,
                                fill_oauth_scope: true,
-                               **kw)
+                               credentials: call_creds)
     json_key = File.read(ENV[AUTH_ENV])
     wanted_email = MultiJson.load(json_key)['client_email']
     assert("#{__callee__}: bad username") { wanted_email == resp.username }
diff --git a/src/ruby/spec/call_spec.rb b/src/ruby/spec/call_spec.rb
index 6629570..ae3ce07 100644
--- a/src/ruby/spec/call_spec.rb
+++ b/src/ruby/spec/call_spec.rb
@@ -101,7 +101,7 @@
   let(:fake_host) { 'localhost:10101' }
 
   before(:each) do
-    @ch = GRPC::Core::Channel.new(fake_host, nil)
+    @ch = GRPC::Core::Channel.new(fake_host, nil, :this_channel_is_insecure)
   end
 
   describe '#status' do
diff --git a/src/ruby/spec/channel_spec.rb b/src/ruby/spec/channel_spec.rb
index b4d2b94..355f95c 100644
--- a/src/ruby/spec/channel_spec.rb
+++ b/src/ruby/spec/channel_spec.rb
@@ -45,7 +45,10 @@
 
   shared_examples '#new' do
     it 'take a host name without channel args' do
-      expect { GRPC::Core::Channel.new('dummy_host', nil) }.not_to raise_error
+      blk = proc do
+        GRPC::Core::Channel.new('dummy_host', nil, :this_channel_is_insecure)
+      end
+      expect(&blk).not_to raise_error
     end
 
     it 'does not take a hash with bad keys as channel args' do
@@ -106,13 +109,15 @@
     it_behaves_like '#new'
 
     def construct_with_args(a)
-      proc { GRPC::Core::Channel.new('dummy_host', a) }
+      proc do
+        GRPC::Core::Channel.new('dummy_host', a, :this_channel_is_insecure)
+      end
     end
   end
 
   describe '#create_call' do
     it 'creates a call OK' do
-      ch = GRPC::Core::Channel.new(fake_host, nil)
+      ch = GRPC::Core::Channel.new(fake_host, nil, :this_channel_is_insecure)
 
       deadline = Time.now + 5
 
@@ -123,7 +128,7 @@
     end
 
     it 'raises an error if called on a closed channel' do
-      ch = GRPC::Core::Channel.new(fake_host, nil)
+      ch = GRPC::Core::Channel.new(fake_host, nil, :this_channel_is_insecure)
       ch.close
 
       deadline = Time.now + 5
@@ -136,13 +141,13 @@
 
   describe '#destroy' do
     it 'destroys a channel ok' do
-      ch = GRPC::Core::Channel.new(fake_host, nil)
+      ch = GRPC::Core::Channel.new(fake_host, nil, :this_channel_is_insecure)
       blk = proc { ch.destroy }
       expect(&blk).to_not raise_error
     end
 
     it 'can be called more than once without error' do
-      ch = GRPC::Core::Channel.new(fake_host, nil)
+      ch = GRPC::Core::Channel.new(fake_host, nil, :this_channel_is_insecure)
       blk = proc { ch.destroy }
       blk.call
       expect(&blk).to_not raise_error
@@ -157,13 +162,13 @@
 
   describe '#close' do
     it 'closes a channel ok' do
-      ch = GRPC::Core::Channel.new(fake_host, nil)
+      ch = GRPC::Core::Channel.new(fake_host, nil, :this_channel_is_insecure)
       blk = proc { ch.close }
       expect(&blk).to_not raise_error
     end
 
     it 'can be called more than once without error' do
-      ch = GRPC::Core::Channel.new(fake_host, nil)
+      ch = GRPC::Core::Channel.new(fake_host, nil, :this_channel_is_insecure)
       blk = proc { ch.close }
       blk.call
       expect(&blk).to_not raise_error
diff --git a/src/ruby/spec/client_server_spec.rb b/src/ruby/spec/client_server_spec.rb
index 7cce207..594fda1 100644
--- a/src/ruby/spec/client_server_spec.rb
+++ b/src/ruby/spec/client_server_spec.rb
@@ -397,7 +397,7 @@
     @server = GRPC::Core::Server.new(@server_queue, nil)
     server_port = @server.add_http2_port(server_host, :this_port_is_insecure)
     @server.start
-    @ch = Channel.new("0.0.0.0:#{server_port}", nil)
+    @ch = Channel.new("0.0.0.0:#{server_port}", nil, :this_channel_is_insecure)
   end
 
   after(:example) do
diff --git a/src/ruby/spec/generic/active_call_spec.rb b/src/ruby/spec/generic/active_call_spec.rb
index b05e328..c0181e2 100644
--- a/src/ruby/spec/generic/active_call_spec.rb
+++ b/src/ruby/spec/generic/active_call_spec.rb
@@ -48,7 +48,8 @@
     @server = GRPC::Core::Server.new(@server_queue, nil)
     server_port = @server.add_http2_port(host, :this_port_is_insecure)
     @server.start
-    @ch = GRPC::Core::Channel.new("0.0.0.0:#{server_port}", nil)
+    @ch = GRPC::Core::Channel.new("0.0.0.0:#{server_port}", nil,
+                                  :this_channel_is_insecure)
   end
 
   after(:each) do
diff --git a/src/ruby/spec/generic/client_stub_spec.rb b/src/ruby/spec/generic/client_stub_spec.rb
index 4055023..5e13c25 100644
--- a/src/ruby/spec/generic/client_stub_spec.rb
+++ b/src/ruby/spec/generic/client_stub_spec.rb
@@ -70,7 +70,7 @@
     it 'can be created from a host and args' do
       opts = { a_channel_arg: 'an_arg' }
       blk = proc do
-        GRPC::ClientStub.new(fake_host, @cq, **opts)
+        GRPC::ClientStub.new(fake_host, @cq, :this_channel_is_insecure, **opts)
       end
       expect(&blk).not_to raise_error
     end
@@ -78,7 +78,7 @@
     it 'can be created with a default deadline' do
       opts = { a_channel_arg: 'an_arg', deadline: 5 }
       blk = proc do
-        GRPC::ClientStub.new(fake_host, @cq, **opts)
+        GRPC::ClientStub.new(fake_host, @cq, :this_channel_is_insecure, **opts)
       end
       expect(&blk).not_to raise_error
     end
@@ -86,7 +86,7 @@
     it 'can be created with an channel override' do
       opts = { a_channel_arg: 'an_arg', channel_override: @ch }
       blk = proc do
-        GRPC::ClientStub.new(fake_host, @cq, **opts)
+        GRPC::ClientStub.new(fake_host, @cq, :this_channel_is_insecure, **opts)
       end
       expect(&blk).not_to raise_error
     end
@@ -94,15 +94,15 @@
     it 'cannot be created with a bad channel override' do
       blk = proc do
         opts = { a_channel_arg: 'an_arg', channel_override: Object.new }
-        GRPC::ClientStub.new(fake_host, @cq, **opts)
+        GRPC::ClientStub.new(fake_host, @cq, :this_channel_is_insecure, **opts)
       end
       expect(&blk).to raise_error
     end
 
     it 'cannot be created with bad credentials' do
       blk = proc do
-        opts = { a_channel_arg: 'an_arg', creds: Object.new }
-        GRPC::ClientStub.new(fake_host, @cq, **opts)
+        opts = { a_channel_arg: 'an_arg' }
+        GRPC::ClientStub.new(fake_host, @cq, Object.new, **opts)
       end
       expect(&blk).to raise_error
     end
@@ -112,10 +112,10 @@
       blk = proc do
         opts = {
           GRPC::Core::Channel::SSL_TARGET => 'foo.test.google.fr',
-          a_channel_arg: 'an_arg',
-          creds: GRPC::Core::ChannelCredentials.new(certs[0], nil, nil)
+          a_channel_arg: 'an_arg'
         }
-        GRPC::ClientStub.new(fake_host, @cq, **opts)
+        creds = GRPC::Core::ChannelCredentials.new(certs[0], nil, nil)
+        GRPC::ClientStub.new(fake_host, @cq, creds,  **opts)
       end
       expect(&blk).to_not raise_error
     end
@@ -130,7 +130,8 @@
       it 'should send a request to/receive a reply from a server' do
         server_port = create_test_server
         th = run_request_response(@sent_msg, @resp, @pass)
-        stub = GRPC::ClientStub.new("localhost:#{server_port}", @cq)
+        stub = GRPC::ClientStub.new("localhost:#{server_port}", @cq,
+                                    :this_channel_is_insecure)
         expect(get_response(stub)).to eq(@resp)
         th.join
       end
@@ -140,7 +141,7 @@
         host = "localhost:#{server_port}"
         th = run_request_response(@sent_msg, @resp, @pass,
                                   k1: 'v1', k2: 'v2')
-        stub = GRPC::ClientStub.new(host, @cq)
+        stub = GRPC::ClientStub.new(host, @cq, :this_channel_is_insecure)
         expect(get_response(stub)).to eq(@resp)
         th.join
       end
@@ -149,8 +150,10 @@
         server_port = create_test_server
         alt_host = "localhost:#{server_port}"
         th = run_request_response(@sent_msg, @resp, @pass)
-        ch = GRPC::Core::Channel.new(alt_host, nil)
-        stub = GRPC::ClientStub.new('ignored-host', @cq, channel_override: ch)
+        ch = GRPC::Core::Channel.new(alt_host, nil, :this_channel_is_insecure)
+        stub = GRPC::ClientStub.new('ignored-host', @cq,
+                                    :this_channel_is_insecure,
+                                    channel_override: ch)
         expect(get_response(stub)).to eq(@resp)
         th.join
       end
@@ -159,7 +162,7 @@
         server_port = create_test_server
         host = "localhost:#{server_port}"
         th = run_request_response(@sent_msg, @resp, @fail)
-        stub = GRPC::ClientStub.new(host, @cq)
+        stub = GRPC::ClientStub.new(host, @cq, :this_channel_is_insecure)
         blk = proc { get_response(stub) }
         expect(&blk).to raise_error(GRPC::BadStatus)
         th.join
@@ -198,7 +201,7 @@
         server_port = create_test_server
         host = "localhost:#{server_port}"
         th = run_client_streamer(@sent_msgs, @resp, @pass)
-        stub = GRPC::ClientStub.new(host, @cq)
+        stub = GRPC::ClientStub.new(host, @cq, :this_channel_is_insecure)
         expect(get_response(stub)).to eq(@resp)
         th.join
       end
@@ -208,7 +211,7 @@
         host = "localhost:#{server_port}"
         th = run_client_streamer(@sent_msgs, @resp, @pass,
                                  k1: 'v1', k2: 'v2')
-        stub = GRPC::ClientStub.new(host, @cq)
+        stub = GRPC::ClientStub.new(host, @cq, :this_channel_is_insecure)
         expect(get_response(stub)).to eq(@resp)
         th.join
       end
@@ -217,7 +220,7 @@
         server_port = create_test_server
         host = "localhost:#{server_port}"
         th = run_client_streamer(@sent_msgs, @resp, @fail)
-        stub = GRPC::ClientStub.new(host, @cq)
+        stub = GRPC::ClientStub.new(host, @cq, :this_channel_is_insecure)
         blk = proc { get_response(stub) }
         expect(&blk).to raise_error(GRPC::BadStatus)
         th.join
@@ -256,7 +259,7 @@
         server_port = create_test_server
         host = "localhost:#{server_port}"
         th = run_server_streamer(@sent_msg, @replys, @pass)
-        stub = GRPC::ClientStub.new(host, @cq)
+        stub = GRPC::ClientStub.new(host, @cq, :this_channel_is_insecure)
         expect(get_responses(stub).collect { |r| r }).to eq(@replys)
         th.join
       end
@@ -265,7 +268,7 @@
         server_port = create_test_server
         host = "localhost:#{server_port}"
         th = run_server_streamer(@sent_msg, @replys, @fail)
-        stub = GRPC::ClientStub.new(host, @cq)
+        stub = GRPC::ClientStub.new(host, @cq, :this_channel_is_insecure)
         e = get_responses(stub)
         expect { e.collect { |r| r } }.to raise_error(GRPC::BadStatus)
         th.join
@@ -276,7 +279,7 @@
         host = "localhost:#{server_port}"
         th = run_server_streamer(@sent_msg, @replys, @fail,
                                  k1: 'v1', k2: 'v2')
-        stub = GRPC::ClientStub.new(host, @cq)
+        stub = GRPC::ClientStub.new(host, @cq, :this_channel_is_insecure)
         e = get_responses(stub)
         expect { e.collect { |r| r } }.to raise_error(GRPC::BadStatus)
         th.join
@@ -320,7 +323,7 @@
       it 'supports sending all the requests first', bidi: true do
         th = run_bidi_streamer_handle_inputs_first(@sent_msgs, @replys,
                                                    @pass)
-        stub = GRPC::ClientStub.new(@host, @cq)
+        stub = GRPC::ClientStub.new(@host, @cq, :this_channel_is_insecure)
         e = get_responses(stub)
         expect(e.collect { |r| r }).to eq(@replys)
         th.join
@@ -328,7 +331,7 @@
 
       it 'supports client-initiated ping pong', bidi: true do
         th = run_bidi_streamer_echo_ping_pong(@sent_msgs, @pass, true)
-        stub = GRPC::ClientStub.new(@host, @cq)
+        stub = GRPC::ClientStub.new(@host, @cq, :this_channel_is_insecure)
         e = get_responses(stub)
         expect(e.collect { |r| r }).to eq(@sent_msgs)
         th.join
@@ -336,7 +339,7 @@
 
       it 'supports a server-initiated ping pong', bidi: true do
         th = run_bidi_streamer_echo_ping_pong(@sent_msgs, @pass, false)
-        stub = GRPC::ClientStub.new(@host, @cq)
+        stub = GRPC::ClientStub.new(@host, @cq, :this_channel_is_insecure)
         e = get_responses(stub)
         expect(e.collect { |r| r }).to eq(@sent_msgs)
         th.join
@@ -376,7 +379,7 @@
 
       it 'should fail with DeadlineExceeded', bidi: true do
         @server.start
-        stub = GRPC::ClientStub.new(@host, @cq)
+        stub = GRPC::ClientStub.new(@host, @cq, :this_channel_is_insecure)
         blk = proc do
           e = stub.bidi_streamer(@method, @sent_msgs, noop, noop,
                                  timeout: 0.001)
diff --git a/src/ruby/spec/generic/rpc_server_spec.rb b/src/ruby/spec/generic/rpc_server_spec.rb
index d95a021..be6331d 100644
--- a/src/ruby/spec/generic/rpc_server_spec.rb
+++ b/src/ruby/spec/generic/rpc_server_spec.rb
@@ -141,7 +141,7 @@
     @server = GRPC::Core::Server.new(@server_queue, nil)
     server_port = @server.add_http2_port(server_host, :this_port_is_insecure)
     @host = "localhost:#{server_port}"
-    @ch = GRPC::Core::Channel.new(@host, nil)
+    @ch = GRPC::Core::Channel.new(@host, nil, :this_channel_is_insecure)
   end
 
   describe '#new' do
@@ -355,7 +355,8 @@
         req = EchoMsg.new
         blk = proc do
           cq = GRPC::Core::CompletionQueue.new
-          stub = GRPC::ClientStub.new(@host, cq, **client_opts)
+          stub = GRPC::ClientStub.new(@host, cq, :this_channel_is_insecure,
+                                      **client_opts)
           stub.request_response('/unknown', req, marshal, unmarshal)
         end
         expect(&blk).to raise_error GRPC::BadStatus
@@ -369,7 +370,7 @@
         @srv.wait_till_running
         req = EchoMsg.new
         n = 5  # arbitrary
-        stub = EchoStub.new(@host, **client_opts)
+        stub = EchoStub.new(@host, :this_channel_is_insecure, **client_opts)
         n.times { expect(stub.an_rpc(req)).to be_a(EchoMsg) }
         @srv.stop
         t.join
@@ -381,7 +382,7 @@
         t = Thread.new { @srv.run }
         @srv.wait_till_running
         req = EchoMsg.new
-        stub = EchoStub.new(@host, **client_opts)
+        stub = EchoStub.new(@host, :this_channel_is_insecure, **client_opts)
         expect(stub.an_rpc(req, k1: 'v1', k2: 'v2')).to be_a(EchoMsg)
         wanted_md = [{ 'k1' => 'v1', 'k2' => 'v2' }]
         check_md(wanted_md, service.received_md)
@@ -395,7 +396,7 @@
         t = Thread.new { @srv.run }
         @srv.wait_till_running
         req = EchoMsg.new
-        stub = SlowStub.new(@host, **client_opts)
+        stub = SlowStub.new(@host, :this_channel_is_insecure, **client_opts)
         timeout = service.delay + 1.0 # wait for long enough
         resp = stub.an_rpc(req, timeout: timeout, k1: 'v1', k2: 'v2')
         expect(resp).to be_a(EchoMsg)
@@ -411,7 +412,7 @@
         t = Thread.new { @srv.run }
         @srv.wait_till_running
         req = EchoMsg.new
-        stub = SlowStub.new(@host, **client_opts)
+        stub = SlowStub.new(@host, :this_channel_is_insecure, **client_opts)
         op = stub.an_rpc(req, k1: 'v1', k2: 'v2', return_op: true)
         Thread.new do  # cancel the call
           sleep 0.1
@@ -431,7 +432,7 @@
         threads = [t]
         n.times do
           threads << Thread.new do
-            stub = EchoStub.new(@host, **client_opts)
+            stub = EchoStub.new(@host, :this_channel_is_insecure, **client_opts)
             q << stub.an_rpc(req)
           end
         end
@@ -459,7 +460,7 @@
         one_failed_as_unavailable = false
         n.times do
           threads << Thread.new do
-            stub = SlowStub.new(@host, **client_opts)
+            stub = SlowStub.new(@host, :this_channel_is_insecure, **client_opts)
             begin
               stub.an_rpc(req)
             rescue GRPC::BadStatus => e
@@ -499,7 +500,7 @@
         t = Thread.new { @srv.run }
         @srv.wait_till_running
         req = EchoMsg.new
-        stub = EchoStub.new(@host, **client_opts)
+        stub = EchoStub.new(@host, :this_channel_is_insecure, **client_opts)
         op = stub.an_rpc(req, k1: 'v1', k2: 'v2', return_op: true)
         expect(op.metadata).to be nil
         expect(op.execute).to be_a(EchoMsg)
@@ -537,7 +538,7 @@
         t = Thread.new { @srv.run }
         @srv.wait_till_running
         req = EchoMsg.new
-        stub = FailingStub.new(@host, **client_opts)
+        stub = FailingStub.new(@host, :this_channel_is_insecure, **client_opts)
         blk = proc { stub.an_rpc(req) }
 
         # confirm it raise the expected error
@@ -562,7 +563,7 @@
         t = Thread.new { @srv.run }
         @srv.wait_till_running
         req = EchoMsg.new
-        stub = EchoStub.new(@host, **client_opts)
+        stub = EchoStub.new(@host, :this_channel_is_insecure, **client_opts)
         op = stub.an_rpc(req, k1: 'v1', k2: 'v2', return_op: true)
         expect(op.metadata).to be nil
         expect(op.execute).to be_a(EchoMsg)
diff --git a/src/ruby/spec/generic/service_spec.rb b/src/ruby/spec/generic/service_spec.rb
index 6cfc34d..5e7b6c7 100644
--- a/src/ruby/spec/generic/service_spec.rb
+++ b/src/ruby/spec/generic/service_spec.rb
@@ -241,7 +241,7 @@
     end
 
     describe 'the generated instances' do
-      it 'can be instanciated with just a hostname' do
+      it 'can be instanciated with just a hostname and credentials' do
         s = Class.new do
           include GenericService
           rpc :AnRpc, GoodMsg, GoodMsg
@@ -250,7 +250,10 @@
           rpc :ABidiStreamer, stream(GoodMsg), stream(GoodMsg)
         end
         client_class = s.rpc_stub_class
-        expect { client_class.new('fakehostname') }.not_to raise_error
+        blk = proc do
+          client_class.new('fakehostname', :this_channel_is_insecure)
+        end
+        expect(&blk).not_to raise_error
       end
 
       it 'has the methods defined in the service' do
@@ -262,7 +265,7 @@
           rpc :ABidiStreamer, stream(GoodMsg), stream(GoodMsg)
         end
         client_class = s.rpc_stub_class
-        o = client_class.new('fakehostname')
+        o = client_class.new('fakehostname', :this_channel_is_insecure)
         expect(o.methods).to include(:an_rpc)
         expect(o.methods).to include(:a_bidi_streamer)
         expect(o.methods).to include(:a_client_streamer)
diff --git a/src/ruby/spec/pb/health/checker_spec.rb b/src/ruby/spec/pb/health/checker_spec.rb
index 322566b..c1decd8 100644
--- a/src/ruby/spec/pb/health/checker_spec.rb
+++ b/src/ruby/spec/pb/health/checker_spec.rb
@@ -1,4 +1,4 @@
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -47,13 +47,12 @@
       end
 
       it 'should have the same content as created by code generation' do
-        root_dir = File.dirname(
-          File.dirname(File.dirname(File.dirname(__FILE__))))
-        pb_dir = File.join(root_dir, 'pb')
+        root_dir = File.join(File.dirname(__FILE__), '..', '..', '..', '..')
+        pb_dir = File.join(root_dir, 'proto')
 
         # Get the current content
-        service_path = File.join(pb_dir, 'grpc', 'health', 'v1alpha',
-                                 'health_services.rb')
+        service_path = File.join(root_dir, 'ruby', 'pb', 'grpc',
+                                 'health', 'v1alpha', 'health_services.rb')
         want = nil
         File.open(service_path) { |f| want = f.read }
 
@@ -188,7 +187,7 @@
       @server = GRPC::Core::Server.new(@server_queue, nil)
       server_port = @server.add_http2_port(server_host, :this_port_is_insecure)
       @host = "localhost:#{server_port}"
-      @ch = GRPC::Core::Channel.new(@host, nil)
+      @ch = GRPC::Core::Channel.new(@host, nil, :this_channel_is_insecure)
       @client_opts = { channel_override: @ch }
       server_opts = {
         server_override: @server,
@@ -208,7 +207,7 @@
       t = Thread.new { @srv.run }
       @srv.wait_till_running
 
-      stub = CheckerStub.new(@host, **@client_opts)
+      stub = CheckerStub.new(@host, :this_channel_is_insecure, **@client_opts)
       got = stub.check(HCReq.new)
       want = HCResp.new(status: ServingStatus::NOT_SERVING)
       expect(got).to eq(want)
@@ -221,7 +220,7 @@
       t = Thread.new { @srv.run }
       @srv.wait_till_running
       blk = proc do
-        stub = CheckerStub.new(@host, **@client_opts)
+        stub = CheckerStub.new(@host, :this_channel_is_insecure, **@client_opts)
         stub.check(HCReq.new(host: 'unknown', service: 'unknown'))
       end
       expected_msg = /#{StatusCodes::NOT_FOUND}/
diff --git a/templates/Makefile.template b/templates/Makefile.template
index 43ce6c5..9890288 100644
--- a/templates/Makefile.template
+++ b/templates/Makefile.template
@@ -7,7 +7,7 @@
   # This file can be regenerated from the template by running
   # tools/buildgen/generate_projects.sh
 
-  # Copyright 2015, Google Inc.
+  # Copyright 2015-2016, Google Inc.
   # All rights reserved.
   #
   # Redistribution and use in source and binary forms, with or without
diff --git a/templates/grpc.gemspec.template b/templates/grpc.gemspec.template
new file mode 100644
index 0000000..fdf87ee
--- /dev/null
+++ b/templates/grpc.gemspec.template
@@ -0,0 +1,58 @@
+%YAML 1.2
+--- |
+  # -*- ruby -*-
+  # encoding: utf-8
+  $LOAD_PATH.push File.expand_path('../src/ruby/lib', __FILE__)
+  require 'grpc/version'
+
+  Gem::Specification.new do |s|
+    s.name          = 'grpc'
+    s.version       = GRPC::VERSION
+    s.authors       = ['gRPC Authors']
+    s.email         = 'temiola@google.com'
+    s.homepage      = 'https://github.com/google/grpc/tree/master/src/ruby'
+    s.summary       = 'GRPC system in Ruby'
+    s.description   = 'Send RPCs from Ruby using GRPC'
+    s.license       = 'BSD-3-Clause'
+
+    s.required_ruby_version = '>= 2.0.0'
+    s.requirements << 'libgrpc ~> 0.11.0 needs to be installed'
+
+    s.files = %w( Rakefile Makefile )
+    s.files += %w( etc/roots.pem )
+    s.files += Dir.glob('src/ruby/bin/**/*')
+    s.files += Dir.glob('src/ruby/ext/**/*')
+    s.files += Dir.glob('src/ruby/lib/**/*')
+    s.files += Dir.glob('src/ruby/pb/**/*')
+    s.files += Dir.glob('include/grpc/**/*')
+    s.test_files = Dir.glob('src/ruby/spec/**/*')
+    s.bindir = 'src/ruby/bin'
+    ${'%'}w(math noproto).each do |b|
+      s.executables += ["#{b}_client.rb", "#{b}_server.rb"]
+    end
+    s.executables += %w(grpc_ruby_interop_client grpc_ruby_interop_server)
+    s.require_paths = %w( src/ruby/bin src/ruby/lib src/ruby/pb )
+    s.platform      = Gem::Platform::RUBY
+
+    s.add_dependency 'google-protobuf', '~> 3.0.0alpha.1.1'
+    s.add_dependency 'googleauth', '~> 0.5.1'
+
+    s.add_development_dependency 'bundler', '~> 1.9'
+    s.add_development_dependency 'logging', '~> 2.0'
+    s.add_development_dependency 'simplecov', '~> 0.9'
+    s.add_development_dependency 'rake', '~> 10.4'
+    s.add_development_dependency 'rake-compiler', '~> 0.9'
+    s.add_development_dependency 'rspec', '~> 3.2'
+    s.add_development_dependency 'rubocop', '~> 0.30.0'
+    s.add_development_dependency 'signet', '~>0.7.0'
+
+    s.extensions = %w(src/ruby/ext/grpc/extconf.rb)
+
+    % for lib in libs:
+    % if lib.name in ('gpr', 'grpc'):
+    % for file in lib.public_headers + lib.headers + lib.src:
+    s.files += %w( ${file} )
+    % endfor
+    % endif
+    % endfor
+  end
diff --git a/templates/package.json.template b/templates/package.json.template
new file mode 100644
index 0000000..ec6827e
--- /dev/null
+++ b/templates/package.json.template
@@ -0,0 +1,74 @@
+%YAML 1.2
+--- |
+  {
+    "name": "grpc",
+    "version": "0.12.0",
+    "author": "Google Inc.",
+    "description": "gRPC Library for Node",
+    "homepage": "http://www.grpc.io/",
+    "repository": {
+      "type": "git",
+      "url": "https://github.com/grpc/grpc.git"
+    },
+    "bugs": "https://github.com/grpc/grpc/issues",
+    "contributors": [
+      {
+        "name": "Michael Lumish",
+        "email": "mlumish@google.com"
+      }
+    ],
+    "directories": {
+      "lib": "src/node/src"
+    },
+    "scripts": {
+      "lint": "node ./node_modules/jshint/bin/jshint src/node/src src/node/test src/node/interop src/node/index.js",
+      "test": "./node_modules/.bin/mocha src/node/test && npm run-script lint",
+      "gen_docs": "./node_modules/.bin/jsdoc -c src/node/jsdoc_conf.json",
+      "coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha src/node/test"
+    },
+    "dependencies": {
+      "bindings": "^1.2.0",
+      "lodash": "^3.9.3",
+      "nan": "^2.0.0",
+      "protobufjs": "^4.0.0"
+    },
+    "devDependencies": {
+      "async": "^1.5.0",
+      "google-auth-library": "^0.9.2",
+      "istanbul": "^0.3.21",
+      "jsdoc": "^3.3.2",
+      "jshint": "^2.5.0",
+      "minimist": "^1.1.0",
+      "mocha": "^2.3.4",
+      "mocha-jenkins-reporter": "^0.1.9",
+      "mustache": "^2.0.0",
+      "poisson-process": "^0.2.1"
+    },
+    "engines": {
+      "node": ">=0.10.13"
+    },
+    "files": [
+      "LICENSE",
+      "src/node/README.md",
+      "src/node/health_check",
+      "src/proto",
+      "etc",
+      % for module in node_modules:
+      % for file in module.headers + module.src + module.js:
+      "${file}",
+      % endfor
+      % for dep in module.transitive_deps:
+      % for lib in libs:
+      % if lib.name == dep:
+      % for file in lib.get('public_headers', []) + lib.headers + lib.src:
+      "${file}",
+      % endfor
+      % endif
+      % endfor
+      % endfor
+      % endfor
+      "binding.gyp"
+    ],
+    "main": "src/node/index.js",
+    "license": "BSD-3-Clause"
+  }
diff --git a/templates/src/python/grpcio/grpc_core_dependencies.py.template b/templates/src/python/grpcio/grpc_core_dependencies.py.template
new file mode 100644
index 0000000..2fc7a03
--- /dev/null
+++ b/templates/src/python/grpcio/grpc_core_dependencies.py.template
@@ -0,0 +1,42 @@
+%YAML 1.2
+--- |
+  # Copyright 2015-2016, Google Inc.
+  # All rights reserved.
+  #
+  # Redistribution and use in source and binary forms, with or without
+  # modification, are permitted provided that the following conditions are
+  # met:
+  #
+  #     * Redistributions of source code must retain the above copyright
+  # notice, this list of conditions and the following disclaimer.
+  #     * Redistributions in binary form must reproduce the above
+  # copyright notice, this list of conditions and the following disclaimer
+  # in the documentation and/or other materials provided with the
+  # distribution.
+  #     * Neither the name of Google Inc. nor the names of its
+  # contributors may be used to endorse or promote products derived from
+  # this software without specific prior written permission.
+  #
+  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio/grpc_core_dependencies.py.template`!!!
+
+  CORE_SOURCE_FILES = [
+  % for lib in libs:
+  % if lib.name in python_dependencies.transitive_deps:
+  % for src in lib.src:
+    '${src}',
+  % endfor
+  % endif
+  % endfor
+  ]
diff --git a/test/core/iomgr/tcp_posix_test.c b/test/core/iomgr/tcp_posix_test.c
index b59ba10..e0136b3 100644
--- a/test/core/iomgr/tcp_posix_test.c
+++ b/test/core/iomgr/tcp_posix_test.c
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/test/core/security/jwt_verifier_test.c b/test/core/security/jwt_verifier_test.c
index 9f37e03..f396398 100644
--- a/test/core/security/jwt_verifier_test.c
+++ b/test/core/security/jwt_verifier_test.c
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/test/core/support/cpu_test.c b/test/core/support/cpu_test.c
index a5c5244..da16f13 100644
--- a/test/core/support/cpu_test.c
+++ b/test/core/support/cpu_test.c
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/test/core/transport/chttp2/bin_encoder_test.c b/test/core/transport/chttp2/bin_encoder_test.c
index 1ffd8ed..d183807 100644
--- a/test/core/transport/chttp2/bin_encoder_test.c
+++ b/test/core/transport/chttp2/bin_encoder_test.c
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -35,6 +35,10 @@
 
 #include <string.h>
 
+/* This is here for grpc_is_binary_header
+ * TODO(murgatroid99): Remove this
+ */
+#include <grpc/grpc.h>
 #include "src/core/support/string.h"
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
diff --git a/test/cpp/end2end/async_end2end_test.cc b/test/cpp/end2end/async_end2end_test.cc
index dc07eb6..cfda571 100644
--- a/test/cpp/end2end/async_end2end_test.cc
+++ b/test/cpp/end2end/async_end2end_test.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -33,21 +33,21 @@
 
 #include <memory>
 
-#include <grpc/grpc.h>
-#include <grpc/support/thd.h>
-#include <grpc/support/time.h>
 #include <grpc++/channel.h>
 #include <grpc++/client_context.h>
 #include <grpc++/create_channel.h>
 #include <grpc++/server.h>
 #include <grpc++/server_builder.h>
 #include <grpc++/server_context.h>
+#include <grpc/grpc.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
 #include <gtest/gtest.h>
 
-#include "test/core/util/port.h"
-#include "test/core/util/test_config.h"
 #include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h"
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
 #include "test/cpp/util/string_ref_helper.h"
 
 #ifdef GPR_POSIX_SOCKET
@@ -186,7 +186,7 @@
     build_bad.AddListeningPort(server_address_.str(),
                                grpc::InsecureServerCredentials());
     build_bad.RegisterAsyncService(&service_);
-    grpc::testing::TestService::Service sync_service;
+    grpc::testing::EchoTestService::Service sync_service;
     build_bad.RegisterService(&sync_service);
     GPR_ASSERT(build_bad.BuildAndStart() == nullptr);
 
@@ -211,7 +211,7 @@
   void ResetStub() {
     std::shared_ptr<Channel> channel =
         CreateChannel(server_address_.str(), InsecureChannelCredentials());
-    stub_ = grpc::testing::TestService::NewStub(channel);
+    stub_ = grpc::testing::EchoTestService::NewStub(channel);
   }
 
   void SendRpc(int num_rpcs) {
@@ -249,9 +249,9 @@
   }
 
   std::unique_ptr<ServerCompletionQueue> cq_;
-  std::unique_ptr<grpc::testing::TestService::Stub> stub_;
+  std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
   std::unique_ptr<Server> server_;
-  grpc::testing::TestService::AsyncService service_;
+  grpc::testing::EchoTestService::AsyncService service_;
   std::ostringstream server_address_;
 };
 
diff --git a/test/cpp/end2end/client_crash_test.cc b/test/cpp/end2end/client_crash_test.cc
index 68c63b2..5ca5cd7 100644
--- a/test/cpp/end2end/client_crash_test.cc
+++ b/test/cpp/end2end/client_crash_test.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,21 +31,21 @@
  *
  */
 
-#include <grpc/grpc.h>
-#include <grpc/support/thd.h>
-#include <grpc/support/time.h>
 #include <grpc++/channel.h>
 #include <grpc++/client_context.h>
 #include <grpc++/create_channel.h>
 #include <grpc++/server.h>
 #include <grpc++/server_builder.h>
 #include <grpc++/server_context.h>
+#include <grpc/grpc.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
 #include <gtest/gtest.h>
 
-#include "test/core/util/port.h"
-#include "test/core/util/test_config.h"
 #include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h"
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
 #include "test/cpp/util/subprocess.h"
 
 using grpc::testing::EchoRequest;
@@ -63,7 +63,7 @@
  protected:
   CrashTest() {}
 
-  std::unique_ptr<grpc::testing::TestService::Stub> CreateServerAndStub() {
+  std::unique_ptr<grpc::testing::EchoTestService::Stub> CreateServerAndStub() {
     auto port = grpc_pick_unused_port_or_die();
     std::ostringstream addr_stream;
     addr_stream << "localhost:" << port;
@@ -72,7 +72,7 @@
         g_root + "/client_crash_test_server", "--address=" + addr,
     }));
     GPR_ASSERT(server_);
-    return grpc::testing::TestService::NewStub(
+    return grpc::testing::EchoTestService::NewStub(
         CreateChannel(addr, InsecureChannelCredentials()));
   }
 
diff --git a/test/cpp/end2end/client_crash_test_server.cc b/test/cpp/end2end/client_crash_test_server.cc
index f9a9c18..1ec641c 100644
--- a/test/cpp/end2end/client_crash_test_server.cc
+++ b/test/cpp/end2end/client_crash_test_server.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,10 +31,10 @@
  *
  */
 
+#include <gflags/gflags.h>
 #include <iostream>
 #include <memory>
 #include <string>
-#include <gflags/gflags.h>
 
 #include <grpc++/server.h>
 #include <grpc++/server_builder.h>
@@ -56,7 +56,8 @@
 namespace grpc {
 namespace testing {
 
-class ServiceImpl GRPC_FINAL : public ::grpc::testing::TestService::Service {
+class ServiceImpl GRPC_FINAL
+    : public ::grpc::testing::EchoTestService::Service {
   Status BidiStream(ServerContext* context,
                     ServerReaderWriter<EchoResponse, EchoRequest>* stream)
       GRPC_OVERRIDE {
diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc
index 28f8a8e..f8027bc 100644
--- a/test/cpp/end2end/end2end_test.cc
+++ b/test/cpp/end2end/end2end_test.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -34,9 +34,6 @@
 #include <mutex>
 #include <thread>
 
-#include <grpc/grpc.h>
-#include <grpc/support/thd.h>
-#include <grpc/support/time.h>
 #include <grpc++/channel.h>
 #include <grpc++/client_context.h>
 #include <grpc++/create_channel.h>
@@ -46,14 +43,17 @@
 #include <grpc++/server.h>
 #include <grpc++/server_builder.h>
 #include <grpc++/server_context.h>
+#include <grpc/grpc.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
 #include <gtest/gtest.h>
 
 #include "src/core/security/credentials.h"
+#include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h"
+#include "src/proto/grpc/testing/echo.grpc.pb.h"
 #include "test/core/end2end/data/ssl_test_data.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
-#include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h"
-#include "src/proto/grpc/testing/echo.grpc.pb.h"
 #include "test/cpp/util/string_ref_helper.h"
 
 using grpc::testing::EchoRequest;
@@ -196,10 +196,10 @@
 const char TestAuthMetadataProcessor::kGoodGuy[] = "Dr Jekyll";
 const char TestAuthMetadataProcessor::kIdentityPropName[] = "novel identity";
 
-class Proxy : public ::grpc::testing::TestService::Service {
+class Proxy : public ::grpc::testing::EchoTestService::Service {
  public:
   Proxy(std::shared_ptr<Channel> channel)
-      : stub_(grpc::testing::TestService::NewStub(channel)) {}
+      : stub_(grpc::testing::EchoTestService::NewStub(channel)) {}
 
   Status Echo(ServerContext* server_context, const EchoRequest* request,
               EchoResponse* response) GRPC_OVERRIDE {
@@ -209,10 +209,10 @@
   }
 
  private:
-  std::unique_ptr< ::grpc::testing::TestService::Stub> stub_;
+  std::unique_ptr< ::grpc::testing::EchoTestService::Stub> stub_;
 };
 
-class TestServiceImpl : public ::grpc::testing::TestService::Service {
+class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
  public:
   TestServiceImpl() : signal_client_(false), host_() {}
   explicit TestServiceImpl(const grpc::string& host)
@@ -344,7 +344,7 @@
 };
 
 class TestServiceImplDupPkg
-    : public ::grpc::testing::duplicate::TestService::Service {
+    : public ::grpc::testing::duplicate::EchoTestService::Service {
  public:
   Status Echo(ServerContext* context, const EchoRequest* request,
               EchoResponse* response) GRPC_OVERRIDE {
@@ -435,12 +435,12 @@
       channel_ = CreateChannel(proxyaddr.str(), InsecureChannelCredentials());
     }
 
-    stub_ = grpc::testing::TestService::NewStub(channel_);
+    stub_ = grpc::testing::EchoTestService::NewStub(channel_);
   }
 
   bool is_server_started_;
   std::shared_ptr<Channel> channel_;
-  std::unique_ptr<grpc::testing::TestService::Stub> stub_;
+  std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
   std::unique_ptr<Server> server_;
   std::unique_ptr<Server> proxy_server_;
   std::unique_ptr<Proxy> proxy_service_;
@@ -451,7 +451,7 @@
   TestServiceImplDupPkg dup_pkg_service_;
 };
 
-static void SendRpc(grpc::testing::TestService::Stub* stub, int num_rpcs) {
+static void SendRpc(grpc::testing::EchoTestService::Stub* stub, int num_rpcs) {
   EchoRequest request;
   EchoResponse response;
   request.set_message("Hello hello hello hello");
@@ -561,8 +561,8 @@
   EXPECT_EQ(response.message(), request.message());
   EXPECT_TRUE(s.ok());
 
-  std::unique_ptr<grpc::testing::duplicate::TestService::Stub> dup_pkg_stub(
-      grpc::testing::duplicate::TestService::NewStub(channel_));
+  std::unique_ptr<grpc::testing::duplicate::EchoTestService::Stub> dup_pkg_stub(
+      grpc::testing::duplicate::EchoTestService::NewStub(channel_));
   ClientContext context2;
   s = dup_pkg_stub->Echo(&context2, request, &response);
   EXPECT_EQ("no package", response.message());
diff --git a/test/cpp/end2end/generic_end2end_test.cc b/test/cpp/end2end/generic_end2end_test.cc
index f8ceca5..c5d9de3 100644
--- a/test/cpp/end2end/generic_end2end_test.cc
+++ b/test/cpp/end2end/generic_end2end_test.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -33,24 +33,24 @@
 
 #include <memory>
 
-#include <grpc/grpc.h>
-#include <grpc/support/thd.h>
-#include <grpc/support/time.h>
-#include <grpc++/impl/proto_utils.h>
 #include <grpc++/channel.h>
 #include <grpc++/client_context.h>
 #include <grpc++/create_channel.h>
 #include <grpc++/generic/async_generic_service.h>
 #include <grpc++/generic/generic_stub.h>
+#include <grpc++/impl/proto_utils.h>
 #include <grpc++/server.h>
 #include <grpc++/server_builder.h>
 #include <grpc++/server_context.h>
 #include <grpc++/support/slice.h>
+#include <grpc/grpc.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
 #include <gtest/gtest.h>
 
+#include "src/proto/grpc/testing/echo.grpc.pb.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
-#include "src/proto/grpc/testing/echo.grpc.pb.h"
 
 using grpc::testing::EchoRequest;
 using grpc::testing::EchoResponse;
@@ -134,7 +134,7 @@
   void client_fail(int i) { verify_ok(&cli_cq_, i, false); }
 
   void SendRpc(int num_rpcs) {
-    const grpc::string kMethodName("/grpc.cpp.test.util.TestService/Echo");
+    const grpc::string kMethodName("/grpc.cpp.test.util.EchoTestService/Echo");
     for (int i = 0; i < num_rpcs; i++) {
       EchoRequest send_request;
       EchoRequest recv_request;
@@ -193,7 +193,7 @@
 
   CompletionQueue cli_cq_;
   std::unique_ptr<ServerCompletionQueue> srv_cq_;
-  std::unique_ptr<grpc::testing::TestService::Stub> stub_;
+  std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
   std::unique_ptr<grpc::GenericStub> generic_stub_;
   std::unique_ptr<Server> server_;
   AsyncGenericService generic_service_;
@@ -215,7 +215,8 @@
 TEST_F(GenericEnd2endTest, SimpleBidiStreaming) {
   ResetStub();
 
-  const grpc::string kMethodName("/grpc.cpp.test.util.TestService/BidiStream");
+  const grpc::string kMethodName(
+      "/grpc.cpp.test.util.EchoTestService/BidiStream");
   EchoRequest send_request;
   EchoRequest recv_request;
   EchoResponse send_response;
diff --git a/test/cpp/end2end/mock_test.cc b/test/cpp/end2end/mock_test.cc
index dddd1d0..1d29096 100644
--- a/test/cpp/end2end/mock_test.cc
+++ b/test/cpp/end2end/mock_test.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -33,25 +33,25 @@
 
 #include <thread>
 
-#include <grpc/grpc.h>
-#include <grpc/support/thd.h>
-#include <grpc/support/time.h>
 #include <grpc++/channel.h>
 #include <grpc++/client_context.h>
 #include <grpc++/create_channel.h>
 #include <grpc++/server.h>
 #include <grpc++/server_builder.h>
 #include <grpc++/server_context.h>
+#include <grpc/grpc.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
 #include <gtest/gtest.h>
 
-#include "test/core/util/port.h"
-#include "test/core/util/test_config.h"
 #include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h"
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
 
 using grpc::testing::EchoRequest;
 using grpc::testing::EchoResponse;
-using grpc::testing::TestService;
+using grpc::testing::EchoTestService;
 using std::chrono::system_clock;
 
 namespace grpc {
@@ -98,7 +98,7 @@
 };
 
 // Mocked stub.
-class MockStub : public TestService::StubInterface {
+class MockStub : public EchoTestService::StubInterface {
  public:
   MockStub() {}
   ~MockStub() {}
@@ -154,7 +154,7 @@
 
 class FakeClient {
  public:
-  explicit FakeClient(TestService::StubInterface* stub) : stub_(stub) {}
+  explicit FakeClient(EchoTestService::StubInterface* stub) : stub_(stub) {}
 
   void DoEcho() {
     ClientContext context;
@@ -197,13 +197,13 @@
     EXPECT_TRUE(s.ok());
   }
 
-  void ResetStub(TestService::StubInterface* stub) { stub_ = stub; }
+  void ResetStub(EchoTestService::StubInterface* stub) { stub_ = stub; }
 
  private:
-  TestService::StubInterface* stub_;
+  EchoTestService::StubInterface* stub_;
 };
 
-class TestServiceImpl : public TestService::Service {
+class TestServiceImpl : public EchoTestService::Service {
  public:
   Status Echo(ServerContext* context, const EchoRequest* request,
               EchoResponse* response) GRPC_OVERRIDE {
@@ -245,10 +245,10 @@
   void ResetStub() {
     std::shared_ptr<Channel> channel =
         CreateChannel(server_address_.str(), InsecureChannelCredentials());
-    stub_ = grpc::testing::TestService::NewStub(channel);
+    stub_ = grpc::testing::EchoTestService::NewStub(channel);
   }
 
-  std::unique_ptr<grpc::testing::TestService::Stub> stub_;
+  std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
   std::unique_ptr<Server> server_;
   std::ostringstream server_address_;
   TestServiceImpl service_;
diff --git a/test/cpp/end2end/server_crash_test.cc b/test/cpp/end2end/server_crash_test.cc
index 87ce636..9bf9423 100644
--- a/test/cpp/end2end/server_crash_test.cc
+++ b/test/cpp/end2end/server_crash_test.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,21 +31,21 @@
  *
  */
 
-#include <grpc/grpc.h>
-#include <grpc/support/thd.h>
-#include <grpc/support/time.h>
 #include <grpc++/channel.h>
 #include <grpc++/client_context.h>
 #include <grpc++/create_channel.h>
 #include <grpc++/server.h>
 #include <grpc++/server_builder.h>
 #include <grpc++/server_context.h>
+#include <grpc/grpc.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
 #include <gtest/gtest.h>
 
+#include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h"
+#include "src/proto/grpc/testing/echo.grpc.pb.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
-#include "src/proto/grpc/testing/echo.grpc.pb.h"
-#include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h"
 #include "test/cpp/util/subprocess.h"
 
 using grpc::testing::EchoRequest;
@@ -59,7 +59,8 @@
 
 namespace {
 
-class ServiceImpl GRPC_FINAL : public ::grpc::testing::TestService::Service {
+class ServiceImpl GRPC_FINAL
+    : public ::grpc::testing::EchoTestService::Service {
  public:
   ServiceImpl() : bidi_stream_count_(0), response_stream_count_(0) {}
 
diff --git a/test/cpp/end2end/server_crash_test_client.cc b/test/cpp/end2end/server_crash_test_client.cc
index b349ae7..b0e6ac6 100644
--- a/test/cpp/end2end/server_crash_test_client.cc
+++ b/test/cpp/end2end/server_crash_test_client.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,11 +31,11 @@
  *
  */
 
+#include <gflags/gflags.h>
 #include <iostream>
 #include <memory>
 #include <sstream>
 #include <string>
-#include <gflags/gflags.h>
 
 #include <grpc++/channel.h>
 #include <grpc++/client_context.h>
@@ -57,7 +57,7 @@
 
 int main(int argc, char** argv) {
   ParseCommandLineFlags(&argc, &argv, true);
-  auto stub = grpc::testing::TestService::NewStub(
+  auto stub = grpc::testing::EchoTestService::NewStub(
       grpc::CreateChannel(FLAGS_address, grpc::InsecureChannelCredentials()));
 
   EchoRequest request;
diff --git a/test/cpp/end2end/shutdown_test.cc b/test/cpp/end2end/shutdown_test.cc
index 50d4f6d..dbbda3a 100644
--- a/test/cpp/end2end/shutdown_test.cc
+++ b/test/cpp/end2end/shutdown_test.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -33,20 +33,20 @@
 
 #include <thread>
 
-#include <grpc/grpc.h>
-#include <grpc/support/sync.h>
 #include <grpc++/channel.h>
 #include <grpc++/client_context.h>
 #include <grpc++/create_channel.h>
 #include <grpc++/server.h>
 #include <grpc++/server_builder.h>
 #include <grpc++/server_context.h>
+#include <grpc/grpc.h>
+#include <grpc/support/sync.h>
 #include <gtest/gtest.h>
 
 #include "src/core/support/env.h"
-#include "test/core/util/test_config.h"
-#include "test/core/util/port.h"
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
 
 using grpc::testing::EchoRequest;
 using grpc::testing::EchoResponse;
@@ -54,7 +54,7 @@
 namespace grpc {
 namespace testing {
 
-class TestServiceImpl : public ::grpc::testing::TestService::Service {
+class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
  public:
   explicit TestServiceImpl(gpr_event* ev) : ev_(ev) {}
 
@@ -94,7 +94,7 @@
   void ResetStub() {
     string target = "dns:localhost:" + to_string(port_);
     channel_ = CreateChannel(target, InsecureChannelCredentials());
-    stub_ = grpc::testing::TestService::NewStub(channel_);
+    stub_ = grpc::testing::EchoTestService::NewStub(channel_);
   }
 
   string to_string(const int number) {
@@ -115,7 +115,7 @@
 
  protected:
   std::shared_ptr<Channel> channel_;
-  std::unique_ptr<grpc::testing::TestService::Stub> stub_;
+  std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
   std::unique_ptr<Server> server_;
   bool shutdown_;
   int port_;
diff --git a/test/cpp/end2end/streaming_throughput_test.cc b/test/cpp/end2end/streaming_throughput_test.cc
index ca56347..4777b88 100644
--- a/test/cpp/end2end/streaming_throughput_test.cc
+++ b/test/cpp/end2end/streaming_throughput_test.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,9 +31,9 @@
  *
  */
 
+#include <time.h>
 #include <mutex>
 #include <thread>
-#include <time.h>
 
 #include <grpc++/channel.h>
 #include <grpc++/client_context.h>
@@ -49,10 +49,10 @@
 #include <grpc/support/time.h>
 #include <gtest/gtest.h>
 
-#include "test/core/util/port.h"
-#include "test/core/util/test_config.h"
 #include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h"
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
 
 using grpc::testing::EchoRequest;
 using grpc::testing::EchoResponse;
@@ -99,7 +99,7 @@
 namespace grpc {
 namespace testing {
 
-class TestServiceImpl : public ::grpc::testing::TestService::Service {
+class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
  public:
   static void BidiStream_Sender(
       ServerReaderWriter<EchoResponse, EchoRequest>* stream,
@@ -161,10 +161,10 @@
   void ResetStub() {
     std::shared_ptr<Channel> channel =
         CreateChannel(server_address_.str(), InsecureChannelCredentials());
-    stub_ = grpc::testing::TestService::NewStub(channel);
+    stub_ = grpc::testing::EchoTestService::NewStub(channel);
   }
 
-  std::unique_ptr<grpc::testing::TestService::Stub> stub_;
+  std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
   std::unique_ptr<Server> server_;
   std::ostringstream server_address_;
   TestServiceImpl service_;
diff --git a/test/cpp/end2end/thread_stress_test.cc b/test/cpp/end2end/thread_stress_test.cc
index 8efc645..4e8860e 100644
--- a/test/cpp/end2end/thread_stress_test.cc
+++ b/test/cpp/end2end/thread_stress_test.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -34,21 +34,21 @@
 #include <mutex>
 #include <thread>
 
-#include <grpc/grpc.h>
-#include <grpc/support/thd.h>
-#include <grpc/support/time.h>
 #include <grpc++/channel.h>
 #include <grpc++/client_context.h>
 #include <grpc++/create_channel.h>
 #include <grpc++/server.h>
 #include <grpc++/server_builder.h>
 #include <grpc++/server_context.h>
+#include <grpc/grpc.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
 #include <gtest/gtest.h>
 
-#include "test/core/util/port.h"
-#include "test/core/util/test_config.h"
 #include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h"
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
 
 using grpc::testing::EchoRequest;
 using grpc::testing::EchoResponse;
@@ -74,7 +74,7 @@
 
 }  // namespace
 
-class TestServiceImpl : public ::grpc::testing::TestService::Service {
+class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
  public:
   TestServiceImpl() : signal_client_(false) {}
 
@@ -159,7 +159,7 @@
 };
 
 class TestServiceImplDupPkg
-    : public ::grpc::testing::duplicate::TestService::Service {
+    : public ::grpc::testing::duplicate::EchoTestService::Service {
  public:
   Status Echo(ServerContext* context, const EchoRequest* request,
               EchoResponse* response) GRPC_OVERRIDE {
@@ -191,10 +191,10 @@
   void ResetStub() {
     std::shared_ptr<Channel> channel =
         CreateChannel(server_address_.str(), InsecureChannelCredentials());
-    stub_ = grpc::testing::TestService::NewStub(channel);
+    stub_ = grpc::testing::EchoTestService::NewStub(channel);
   }
 
-  std::unique_ptr<grpc::testing::TestService::Stub> stub_;
+  std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
   std::unique_ptr<Server> server_;
   std::ostringstream server_address_;
   const int kMaxMessageSize_;
@@ -202,7 +202,7 @@
   TestServiceImplDupPkg dup_pkg_service_;
 };
 
-static void SendRpc(grpc::testing::TestService::Stub* stub, int num_rpcs) {
+static void SendRpc(grpc::testing::EchoTestService::Stub* stub, int num_rpcs) {
   EchoRequest request;
   EchoResponse response;
   request.set_message("Hello");
diff --git a/test/cpp/end2end/zookeeper_test.cc b/test/cpp/end2end/zookeeper_test.cc
index 802df6b..bbf1b0e 100644
--- a/test/cpp/end2end/zookeeper_test.cc
+++ b/test/cpp/end2end/zookeeper_test.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -37,15 +37,15 @@
 #include <grpc++/server.h>
 #include <grpc++/server_builder.h>
 #include <grpc++/server_context.h>
-#include <gtest/gtest.h>
 #include <grpc/grpc.h>
 #include <grpc/grpc_zookeeper.h>
+#include <gtest/gtest.h>
 #include <zookeeper/zookeeper.h>
 
-#include "test/core/util/test_config.h"
-#include "test/core/util/port.h"
-#include "src/proto/grpc/testing/echo.grpc.pb.h"
 #include "src/core/support/env.h"
+#include "src/proto/grpc/testing/echo.grpc.pb.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
 
 using grpc::testing::EchoRequest;
 using grpc::testing::EchoResponse;
@@ -53,7 +53,8 @@
 namespace grpc {
 namespace testing {
 
-class ZookeeperTestServiceImpl : public ::grpc::testing::TestService::Service {
+class ZookeeperTestServiceImpl
+    : public ::grpc::testing::EchoTestService::Service {
  public:
   Status Echo(ServerContext* context, const EchoRequest* request,
               EchoResponse* response) GRPC_OVERRIDE {
@@ -157,7 +158,7 @@
   void ResetStub() {
     string target = "zookeeper://" + zookeeper_address_ + "/test";
     channel_ = CreateChannel(target, InsecureChannelCredentials());
-    stub_ = grpc::testing::TestService::NewStub(channel_);
+    stub_ = grpc::testing::EchoTestService::NewStub(channel_);
   }
 
   string to_string(const int number) {
@@ -167,7 +168,7 @@
   }
 
   std::shared_ptr<Channel> channel_;
-  std::unique_ptr<grpc::testing::TestService::Stub> stub_;
+  std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
   std::unique_ptr<Server> server1_;
   std::unique_ptr<Server> server2_;
   ZookeeperTestServiceImpl service_;
diff --git a/test/cpp/interop/interop_client.cc b/test/cpp/interop/interop_client.cc
index 6747127..b063107 100644
--- a/test/cpp/interop/interop_client.cc
+++ b/test/cpp/interop/interop_client.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/test/cpp/interop/reconnect_interop_server.cc b/test/cpp/interop/reconnect_interop_server.cc
index e016547..3602b8c 100644
--- a/test/cpp/interop/reconnect_interop_server.cc
+++ b/test/cpp/interop/reconnect_interop_server.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/test/cpp/interop/stress_interop_client.cc b/test/cpp/interop/stress_interop_client.cc
index 04671fb..b581e9b 100644
--- a/test/cpp/interop/stress_interop_client.cc
+++ b/test/cpp/interop/stress_interop_client.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/test/cpp/interop/stress_test.cc b/test/cpp/interop/stress_test.cc
index 05d5f2e..702354d 100644
--- a/test/cpp/interop/stress_test.cc
+++ b/test/cpp/interop/stress_test.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/test/cpp/qps/client.h b/test/cpp/qps/client.h
index ee00495..15cfd7a 100644
--- a/test/cpp/qps/client.h
+++ b/test/cpp/qps/client.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -37,6 +37,9 @@
 #include <condition_variable>
 #include <mutex>
 
+#include <grpc++/support/byte_buffer.h>
+#include <grpc++/support/slice.h>
+
 #include "test/cpp/qps/histogram.h"
 #include "test/cpp/qps/interarrival.h"
 #include "test/cpp/qps/timer.h"
@@ -66,37 +69,64 @@
 typedef std::chrono::high_resolution_clock grpc_time_source;
 typedef std::chrono::time_point<grpc_time_source> grpc_time;
 
-class Client {
+template <class RequestType>
+class ClientRequestCreator {
  public:
-  explicit Client(const ClientConfig& config)
-      : channels_(config.client_channels()),
-        timer_(new Timer),
-        interarrival_timer_() {
-    for (int i = 0; i < config.client_channels(); i++) {
-      channels_[i].init(config.server_targets(i % config.server_targets_size()),
-                        config);
-    }
-    if (config.payload_config().has_bytebuf_params()) {
-      GPR_ASSERT(false);  // not yet implemented
-    } else if (config.payload_config().has_simple_params()) {
-      request_.set_response_type(grpc::testing::PayloadType::COMPRESSABLE);
-      request_.set_response_size(
-          config.payload_config().simple_params().resp_size());
-      request_.mutable_payload()->set_type(
+  ClientRequestCreator(RequestType* req, const PayloadConfig&) {
+    // this template must be specialized
+    // fail with an assertion rather than a compile-time
+    // check since these only happen at the beginning anyway
+    GPR_ASSERT(false);
+  }
+};
+
+template <>
+class ClientRequestCreator<SimpleRequest> {
+ public:
+  ClientRequestCreator(SimpleRequest* req,
+                       const PayloadConfig& payload_config) {
+    if (payload_config.has_bytebuf_params()) {
+      GPR_ASSERT(false);  // not appropriate for this specialization
+    } else if (payload_config.has_simple_params()) {
+      req->set_response_type(grpc::testing::PayloadType::COMPRESSABLE);
+      req->set_response_size(payload_config.simple_params().resp_size());
+      req->mutable_payload()->set_type(
           grpc::testing::PayloadType::COMPRESSABLE);
-      int size = config.payload_config().simple_params().req_size();
+      int size = payload_config.simple_params().req_size();
       std::unique_ptr<char[]> body(new char[size]);
-      request_.mutable_payload()->set_body(body.get(), size);
-    } else if (config.payload_config().has_complex_params()) {
-      GPR_ASSERT(false);  // not yet implemented
+      req->mutable_payload()->set_body(body.get(), size);
+    } else if (payload_config.has_complex_params()) {
+      GPR_ASSERT(false);  // not appropriate for this specialization
     } else {
       // default should be simple proto without payloads
-      request_.set_response_type(grpc::testing::PayloadType::COMPRESSABLE);
-      request_.set_response_size(0);
-      request_.mutable_payload()->set_type(
+      req->set_response_type(grpc::testing::PayloadType::COMPRESSABLE);
+      req->set_response_size(0);
+      req->mutable_payload()->set_type(
           grpc::testing::PayloadType::COMPRESSABLE);
     }
   }
+};
+
+template <>
+class ClientRequestCreator<ByteBuffer> {
+ public:
+  ClientRequestCreator(ByteBuffer* req, const PayloadConfig& payload_config) {
+    if (payload_config.has_bytebuf_params()) {
+      std::unique_ptr<char> buf(
+          new char[payload_config.bytebuf_params().req_size()]);
+      gpr_slice s = gpr_slice_from_copied_buffer(
+          buf.get(), payload_config.bytebuf_params().req_size());
+      Slice slice(s, Slice::STEAL_REF);
+      *req = ByteBuffer(&slice, 1);
+    } else {
+      GPR_ASSERT(false);  // not appropriate for this specialization
+    }
+  }
+};
+
+class Client {
+ public:
+  Client() : timer_(new Timer), interarrival_timer_() {}
   virtual ~Client() {}
 
   ClientStats Mark(bool reset) {
@@ -134,37 +164,8 @@
   }
 
  protected:
-  SimpleRequest request_;
   bool closed_loop_;
 
-  class ClientChannelInfo {
-   public:
-    ClientChannelInfo() {}
-    ClientChannelInfo(const ClientChannelInfo& i) {
-      // The copy constructor is to satisfy old compilers
-      // that need it for using std::vector . It is only ever
-      // used for empty entries
-      GPR_ASSERT(!i.channel_ && !i.stub_);
-    }
-    void init(const grpc::string& target, const ClientConfig& config) {
-      // We have to use a 2-phase init like this with a default
-      // constructor followed by an initializer function to make
-      // old compilers happy with using this in std::vector
-      channel_ = CreateTestChannel(
-          target, config.security_params().server_host_override(),
-          config.has_security_params(),
-          !config.security_params().use_test_ca());
-      stub_ = BenchmarkService::NewStub(channel_);
-    }
-    Channel* get_channel() { return channel_.get(); }
-    BenchmarkService::Stub* get_stub() { return stub_.get(); }
-
-   private:
-    std::shared_ptr<Channel> channel_;
-    std::unique_ptr<BenchmarkService::Stub> stub_;
-  };
-  std::vector<ClientChannelInfo> channels_;
-
   void StartThreads(size_t num_threads) {
     for (size_t i = 0; i < num_threads; i++) {
       threads_.emplace_back(new Thread(this, i));
@@ -295,8 +296,6 @@
       }
     }
 
-    BenchmarkService::Stub* stub_;
-    ClientConfig config_;
     std::mutex mu_;
     std::condition_variable cv_;
     bool done_;
@@ -314,11 +313,66 @@
   std::vector<grpc_time> next_time_;
 };
 
+template <class StubType, class RequestType>
+class ClientImpl : public Client {
+ public:
+  ClientImpl(const ClientConfig& config,
+             std::function<std::unique_ptr<StubType>(std::shared_ptr<Channel>)>
+                 create_stub)
+      : channels_(config.client_channels()), create_stub_(create_stub) {
+    for (int i = 0; i < config.client_channels(); i++) {
+      channels_[i].init(config.server_targets(i % config.server_targets_size()),
+                        config, create_stub_);
+    }
+
+    ClientRequestCreator<RequestType> create_req(&request_,
+                                                 config.payload_config());
+  }
+  virtual ~ClientImpl() {}
+
+ protected:
+  RequestType request_;
+
+  class ClientChannelInfo {
+   public:
+    ClientChannelInfo() {}
+    ClientChannelInfo(const ClientChannelInfo& i) {
+      // The copy constructor is to satisfy old compilers
+      // that need it for using std::vector . It is only ever
+      // used for empty entries
+      GPR_ASSERT(!i.channel_ && !i.stub_);
+    }
+    void init(const grpc::string& target, const ClientConfig& config,
+              std::function<std::unique_ptr<StubType>(std::shared_ptr<Channel>)>
+                  create_stub) {
+      // We have to use a 2-phase init like this with a default
+      // constructor followed by an initializer function to make
+      // old compilers happy with using this in std::vector
+      channel_ = CreateTestChannel(
+          target, config.security_params().server_host_override(),
+          config.has_security_params(),
+          !config.security_params().use_test_ca());
+      stub_ = create_stub(channel_);
+    }
+    Channel* get_channel() { return channel_.get(); }
+    StubType* get_stub() { return stub_.get(); }
+
+   private:
+    std::shared_ptr<Channel> channel_;
+    std::unique_ptr<StubType> stub_;
+  };
+  std::vector<ClientChannelInfo> channels_;
+  std::function<std::unique_ptr<StubType>(const std::shared_ptr<Channel>&)>
+      create_stub_;
+};
+
 std::unique_ptr<Client> CreateSynchronousUnaryClient(const ClientConfig& args);
 std::unique_ptr<Client> CreateSynchronousStreamingClient(
     const ClientConfig& args);
 std::unique_ptr<Client> CreateAsyncUnaryClient(const ClientConfig& args);
 std::unique_ptr<Client> CreateAsyncStreamingClient(const ClientConfig& args);
+std::unique_ptr<Client> CreateGenericAsyncStreamingClient(
+    const ClientConfig& args);
 
 }  // namespace testing
 }  // namespace grpc
diff --git a/test/cpp/qps/client_async.cc b/test/cpp/qps/client_async.cc
index 3aaf065..f270cd0 100644
--- a/test/cpp/qps/client_async.cc
+++ b/test/cpp/qps/client_async.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -37,19 +37,20 @@
 #include <list>
 #include <memory>
 #include <mutex>
+#include <sstream>
 #include <string>
 #include <thread>
 #include <vector>
-#include <sstream>
 
+#include <gflags/gflags.h>
+#include <grpc++/client_context.h>
+#include <grpc++/generic/generic_stub.h>
 #include <grpc/grpc.h>
 #include <grpc/support/histogram.h>
 #include <grpc/support/log.h>
-#include <gflags/gflags.h>
-#include <grpc++/client_context.h>
 
-#include "test/cpp/qps/timer.h"
 #include "test/cpp/qps/client.h"
+#include "test/cpp/qps/timer.h"
 #include "test/cpp/util/create_test_channel.h"
 #include "src/proto/grpc/testing/services.grpc.pb.h"
 
@@ -147,13 +148,22 @@
 
 typedef std::forward_list<ClientRpcContext*> context_list;
 
-class AsyncClient : public Client {
+template <class StubType, class RequestType>
+class AsyncClient : public ClientImpl<StubType, RequestType> {
+  // Specify which protected members we are using since there is no
+  // member name resolution until the template types are fully resolved
  public:
-  explicit AsyncClient(
-      const ClientConfig& config,
-      std::function<ClientRpcContext*(int, BenchmarkService::Stub*,
-                                      const SimpleRequest&)> setup_ctx)
-      : Client(config),
+  using Client::SetupLoadTest;
+  using Client::NextIssueTime;
+  using Client::closed_loop_;
+  using ClientImpl<StubType, RequestType>::channels_;
+  using ClientImpl<StubType, RequestType>::request_;
+  AsyncClient(const ClientConfig& config,
+              std::function<ClientRpcContext*(int, StubType*,
+                                              const RequestType&)> setup_ctx,
+              std::function<std::unique_ptr<StubType>(std::shared_ptr<Channel>)>
+                  create_stub)
+      : ClientImpl<StubType, RequestType>(config, create_stub),
         channel_lock_(new std::mutex[config.client_channels()]),
         contexts_(config.client_channels()),
         max_outstanding_per_channel_(config.outstanding_rpcs_per_channel()),
@@ -343,10 +353,16 @@
   int pref_channel_inc_;
 };
 
-class AsyncUnaryClient GRPC_FINAL : public AsyncClient {
+static std::unique_ptr<BenchmarkService::Stub> BenchmarkStubCreator(
+    std::shared_ptr<Channel> ch) {
+  return BenchmarkService::NewStub(ch);
+}
+
+class AsyncUnaryClient GRPC_FINAL
+    : public AsyncClient<BenchmarkService::Stub, SimpleRequest> {
  public:
   explicit AsyncUnaryClient(const ClientConfig& config)
-      : AsyncClient(config, SetupCtx) {
+      : AsyncClient(config, SetupCtx, BenchmarkStubCreator) {
     StartThreads(config.async_client_threads());
   }
   ~AsyncUnaryClient() GRPC_OVERRIDE { EndThreads(); }
@@ -437,10 +453,11 @@
       stream_;
 };
 
-class AsyncStreamingClient GRPC_FINAL : public AsyncClient {
+class AsyncStreamingClient GRPC_FINAL
+    : public AsyncClient<BenchmarkService::Stub, SimpleRequest> {
  public:
   explicit AsyncStreamingClient(const ClientConfig& config)
-      : AsyncClient(config, SetupCtx) {
+      : AsyncClient(config, SetupCtx, BenchmarkStubCreator) {
     // async streaming currently only supports closed loop
     GPR_ASSERT(closed_loop_);
 
@@ -467,12 +484,119 @@
   }
 };
 
+class ClientRpcContextGenericStreamingImpl : public ClientRpcContext {
+ public:
+  ClientRpcContextGenericStreamingImpl(
+      int channel_id, grpc::GenericStub* stub, const ByteBuffer& req,
+      std::function<std::unique_ptr<grpc::GenericClientAsyncReaderWriter>(
+          grpc::GenericStub*, grpc::ClientContext*,
+          const grpc::string& method_name, CompletionQueue*, void*)> start_req,
+      std::function<void(grpc::Status, ByteBuffer*)> on_done)
+      : ClientRpcContext(channel_id),
+        context_(),
+        stub_(stub),
+        req_(req),
+        response_(),
+        next_state_(&ClientRpcContextGenericStreamingImpl::ReqSent),
+        callback_(on_done),
+        start_req_(start_req),
+        start_(Timer::Now()) {}
+  ~ClientRpcContextGenericStreamingImpl() GRPC_OVERRIDE {}
+  bool RunNextState(bool ok, Histogram* hist) GRPC_OVERRIDE {
+    return (this->*next_state_)(ok, hist);
+  }
+  ClientRpcContext* StartNewClone() GRPC_OVERRIDE {
+    return new ClientRpcContextGenericStreamingImpl(channel_id_, stub_, req_,
+                                                    start_req_, callback_);
+  }
+  void Start(CompletionQueue* cq) GRPC_OVERRIDE {
+    const grpc::string kMethodName(
+        "/grpc.testing.BenchmarkService/StreamingCall");
+    stream_ = start_req_(stub_, &context_, kMethodName, cq,
+                         ClientRpcContext::tag(this));
+  }
+
+ private:
+  bool ReqSent(bool ok, Histogram*) { return StartWrite(ok); }
+  bool StartWrite(bool ok) {
+    if (!ok) {
+      return (false);
+    }
+    start_ = Timer::Now();
+    next_state_ = &ClientRpcContextGenericStreamingImpl::WriteDone;
+    stream_->Write(req_, ClientRpcContext::tag(this));
+    return true;
+  }
+  bool WriteDone(bool ok, Histogram*) {
+    if (!ok) {
+      return (false);
+    }
+    next_state_ = &ClientRpcContextGenericStreamingImpl::ReadDone;
+    stream_->Read(&response_, ClientRpcContext::tag(this));
+    return true;
+  }
+  bool ReadDone(bool ok, Histogram* hist) {
+    hist->Add((Timer::Now() - start_) * 1e9);
+    return StartWrite(ok);
+  }
+  grpc::ClientContext context_;
+  grpc::GenericStub* stub_;
+  ByteBuffer req_;
+  ByteBuffer response_;
+  bool (ClientRpcContextGenericStreamingImpl::*next_state_)(bool, Histogram*);
+  std::function<void(grpc::Status, ByteBuffer*)> callback_;
+  std::function<std::unique_ptr<grpc::GenericClientAsyncReaderWriter>(
+      grpc::GenericStub*, grpc::ClientContext*, const grpc::string&,
+      CompletionQueue*, void*)> start_req_;
+  grpc::Status status_;
+  double start_;
+  std::unique_ptr<grpc::GenericClientAsyncReaderWriter> stream_;
+};
+
+static std::unique_ptr<grpc::GenericStub> GenericStubCreator(
+    std::shared_ptr<Channel> ch) {
+  return std::unique_ptr<grpc::GenericStub>(new grpc::GenericStub(ch));
+}
+
+class GenericAsyncStreamingClient GRPC_FINAL
+    : public AsyncClient<grpc::GenericStub, ByteBuffer> {
+ public:
+  explicit GenericAsyncStreamingClient(const ClientConfig& config)
+      : AsyncClient(config, SetupCtx, GenericStubCreator) {
+    // async streaming currently only supports closed loop
+    GPR_ASSERT(closed_loop_);
+
+    StartThreads(config.async_client_threads());
+  }
+
+  ~GenericAsyncStreamingClient() GRPC_OVERRIDE { EndThreads(); }
+
+ private:
+  static void CheckDone(grpc::Status s, ByteBuffer* response) {}
+  static std::unique_ptr<grpc::GenericClientAsyncReaderWriter> StartReq(
+      grpc::GenericStub* stub, grpc::ClientContext* ctx,
+      const grpc::string& method_name, CompletionQueue* cq, void* tag) {
+    auto stream = stub->Call(ctx, method_name, cq, tag);
+    return stream;
+  };
+  static ClientRpcContext* SetupCtx(int channel_id, grpc::GenericStub* stub,
+                                    const ByteBuffer& req) {
+    return new ClientRpcContextGenericStreamingImpl(
+        channel_id, stub, req, GenericAsyncStreamingClient::StartReq,
+        GenericAsyncStreamingClient::CheckDone);
+  }
+};
+
 std::unique_ptr<Client> CreateAsyncUnaryClient(const ClientConfig& args) {
   return std::unique_ptr<Client>(new AsyncUnaryClient(args));
 }
 std::unique_ptr<Client> CreateAsyncStreamingClient(const ClientConfig& args) {
   return std::unique_ptr<Client>(new AsyncStreamingClient(args));
 }
+std::unique_ptr<Client> CreateGenericAsyncStreamingClient(
+    const ClientConfig& args) {
+  return std::unique_ptr<Client>(new GenericAsyncStreamingClient(args));
+}
 
 }  // namespace testing
 }  // namespace grpc
diff --git a/test/cpp/qps/client_sync.cc b/test/cpp/qps/client_sync.cc
index 9e94f43..92fbf24 100644
--- a/test/cpp/qps/client_sync.cc
+++ b/test/cpp/qps/client_sync.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -35,38 +35,44 @@
 #include <chrono>
 #include <memory>
 #include <mutex>
+#include <sstream>
 #include <string>
 #include <thread>
 #include <vector>
-#include <sstream>
 
 #include <gflags/gflags.h>
+#include <grpc++/client_context.h>
+#include <grpc++/server.h>
+#include <grpc++/server_builder.h>
 #include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/histogram.h>
 #include <grpc/support/host_port.h>
 #include <grpc/support/log.h>
 #include <grpc/support/time.h>
-#include <grpc++/client_context.h>
-#include <grpc++/server.h>
-#include <grpc++/server_builder.h>
 #include <gtest/gtest.h>
 
-#include "test/cpp/util/create_test_channel.h"
+#include "src/core/profiling/timers.h"
+#include "src/proto/grpc/testing/services.grpc.pb.h"
 #include "test/cpp/qps/client.h"
 #include "test/cpp/qps/histogram.h"
 #include "test/cpp/qps/interarrival.h"
 #include "test/cpp/qps/timer.h"
-#include "src/proto/grpc/testing/services.grpc.pb.h"
-
-#include "src/core/profiling/timers.h"
 
 namespace grpc {
 namespace testing {
 
-class SynchronousClient : public Client {
+static std::unique_ptr<BenchmarkService::Stub> BenchmarkStubCreator(
+    std::shared_ptr<Channel> ch) {
+  return BenchmarkService::NewStub(ch);
+}
+
+class SynchronousClient
+    : public ClientImpl<BenchmarkService::Stub, SimpleRequest> {
  public:
-  SynchronousClient(const ClientConfig& config) : Client(config) {
+  SynchronousClient(const ClientConfig& config)
+      : ClientImpl<BenchmarkService::Stub, SimpleRequest>(
+            config, BenchmarkStubCreator) {
     num_threads_ =
         config.outstanding_rpcs_per_channel() * config.client_channels();
     responses_.resize(num_threads_);
diff --git a/test/cpp/qps/generic_async_streaming_ping_pong_test.cc b/test/cpp/qps/generic_async_streaming_ping_pong_test.cc
new file mode 100644
index 0000000..2b2e1c8
--- /dev/null
+++ b/test/cpp/qps/generic_async_streaming_ping_pong_test.cc
@@ -0,0 +1,82 @@
+/*
+ *
+ * Copyright 2015-2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <set>
+
+#include <grpc/support/log.h>
+
+#include "test/cpp/qps/driver.h"
+#include "test/cpp/qps/report.h"
+#include "test/cpp/util/benchmark_config.h"
+
+namespace grpc {
+namespace testing {
+
+static const int WARMUP = 5;
+static const int BENCHMARK = 10;
+
+static void RunGenericAsyncStreamingPingPong() {
+  gpr_log(GPR_INFO, "Running Generic Async Streaming Ping Pong");
+
+  ClientConfig client_config;
+  client_config.set_client_type(ASYNC_CLIENT);
+  client_config.set_outstanding_rpcs_per_channel(1);
+  client_config.set_client_channels(1);
+  client_config.set_async_client_threads(1);
+  client_config.set_rpc_type(STREAMING);
+  client_config.mutable_load_params()->mutable_closed_loop();
+  auto bbuf = client_config.mutable_payload_config()->mutable_bytebuf_params();
+  bbuf->set_resp_size(0);
+  bbuf->set_req_size(0);
+
+  ServerConfig server_config;
+  server_config.set_server_type(ASYNC_SERVER);
+  server_config.set_host("localhost");
+  server_config.set_async_server_threads(1);
+
+  const auto result =
+      RunScenario(client_config, 1, server_config, 1, WARMUP, BENCHMARK, -2);
+
+  GetReporter()->ReportQPS(*result);
+  GetReporter()->ReportLatency(*result);
+}
+
+}  // namespace testing
+}  // namespace grpc
+
+int main(int argc, char** argv) {
+  grpc::testing::InitBenchmark(&argc, &argv, true);
+
+  grpc::testing::RunGenericAsyncStreamingPingPong();
+  return 0;
+}
diff --git a/test/cpp/qps/qps_driver.cc b/test/cpp/qps/qps_driver.cc
index c709639..eb0a7a5 100644
--- a/test/cpp/qps/qps_driver.cc
+++ b/test/cpp/qps/qps_driver.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -62,6 +62,8 @@
 
 DEFINE_int32(simple_req_size, -1, "Simple proto request payload size");
 DEFINE_int32(simple_resp_size, -1, "Simple proto response payload size");
+DEFINE_int32(bbuf_req_size, -1, "Byte-buffer request payload size");
+DEFINE_int32(bbuf_resp_size, -1, "Byte-buffer response payload size");
 
 DEFINE_string(client_type, "SYNC_CLIENT", "Client type");
 DEFINE_int32(async_client_threads, 1, "Async client threads");
@@ -109,6 +111,13 @@
     if (FLAGS_simple_req_size >= 0) {
       params->set_req_size(FLAGS_simple_req_size);
     }
+  } else if (FLAGS_bbuf_resp_size >= 0) {
+    auto params =
+        client_config.mutable_payload_config()->mutable_bytebuf_params();
+    params->set_resp_size(FLAGS_bbuf_resp_size);
+    if (FLAGS_bbuf_req_size >= 0) {
+      params->set_req_size(FLAGS_bbuf_req_size);
+    }
   } else {
     // set a reasonable default: proto but no payload
     client_config.mutable_payload_config()->mutable_simple_params();
diff --git a/test/cpp/qps/qps_worker.cc b/test/cpp/qps/qps_worker.cc
index b3f3832..bed867e 100644
--- a/test/cpp/qps/qps_worker.cc
+++ b/test/cpp/qps/qps_worker.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -36,20 +36,20 @@
 #include <cassert>
 #include <memory>
 #include <mutex>
+#include <sstream>
 #include <string>
 #include <thread>
 #include <vector>
-#include <sstream>
 
+#include <grpc++/client_context.h>
+#include <grpc++/security/server_credentials.h>
+#include <grpc++/server.h>
+#include <grpc++/server_builder.h>
 #include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/histogram.h>
-#include <grpc/support/log.h>
 #include <grpc/support/host_port.h>
-#include <grpc++/client_context.h>
-#include <grpc++/server.h>
-#include <grpc++/server_builder.h>
-#include <grpc++/security/server_credentials.h>
+#include <grpc/support/log.h>
 
 #include "test/core/util/grpc_profiler.h"
 #include "test/cpp/qps/client.h"
@@ -69,7 +69,9 @@
     case ClientType::ASYNC_CLIENT:
       return (config.rpc_type() == RpcType::UNARY)
                  ? CreateAsyncUnaryClient(config)
-                 : CreateAsyncStreamingClient(config);
+                 : (config.payload_config().has_bytebuf_params()
+                        ? CreateGenericAsyncStreamingClient(config)
+                        : CreateAsyncStreamingClient(config));
     default:
       abort();
   }
diff --git a/test/cpp/qps/server.h b/test/cpp/qps/server.h
index 620bc32..32a3e85 100644
--- a/test/cpp/qps/server.h
+++ b/test/cpp/qps/server.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -75,12 +75,11 @@
   }
 
   static bool SetPayload(PayloadType type, int size, Payload* payload) {
-    PayloadType response_type = type;
     // TODO(yangg): Support UNCOMPRESSABLE payload.
     if (type != PayloadType::COMPRESSABLE) {
       return false;
     }
-    payload->set_type(response_type);
+    payload->set_type(type);
     std::unique_ptr<char[]> body(new char[size]());
     payload->set_body(body.get(), size);
     return true;
diff --git a/test/cpp/qps/server_async.cc b/test/cpp/qps/server_async.cc
index 03fde06..d530dac 100644
--- a/test/cpp/qps/server_async.cc
+++ b/test/cpp/qps/server_async.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -38,15 +38,16 @@
 #include <thread>
 
 #include <gflags/gflags.h>
+#include <grpc++/generic/async_generic_service.h>
+#include <grpc++/security/server_credentials.h>
+#include <grpc++/server.h>
+#include <grpc++/server_builder.h>
+#include <grpc++/server_context.h>
+#include <grpc++/support/config.h>
 #include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/host_port.h>
 #include <grpc/support/log.h>
-#include <grpc++/support/config.h>
-#include <grpc++/server.h>
-#include <grpc++/server_builder.h>
-#include <grpc++/server_context.h>
-#include <grpc++/security/server_credentials.h>
 #include <gtest/gtest.h>
 
 #include "test/cpp/qps/server.h"
@@ -55,9 +56,24 @@
 namespace grpc {
 namespace testing {
 
+template <class RequestType, class ResponseType, class ServiceType,
+          class ServerContextType>
 class AsyncQpsServerTest : public Server {
  public:
-  explicit AsyncQpsServerTest(const ServerConfig &config) : Server(config) {
+  AsyncQpsServerTest(
+      const ServerConfig &config,
+      std::function<void(ServerBuilder *, ServiceType *)> register_service,
+      std::function<void(ServiceType *, ServerContextType *, RequestType *,
+                         ServerAsyncResponseWriter<ResponseType> *,
+                         CompletionQueue *, ServerCompletionQueue *, void *)>
+          request_unary_function,
+      std::function<void(ServiceType *, ServerContextType *,
+                         ServerAsyncReaderWriter<ResponseType, RequestType> *,
+                         CompletionQueue *, ServerCompletionQueue *, void *)>
+          request_streaming_function,
+      std::function<grpc::Status(const PayloadConfig &, const RequestType *,
+                                 ResponseType *)> process_rpc)
+      : Server(config) {
     char *server_address = NULL;
 
     gpr_join_host_port(&server_address, config.host().c_str(), port());
@@ -67,7 +83,8 @@
                              Server::CreateServerCredentials(config));
     gpr_free(server_address);
 
-    builder.RegisterAsyncService(&async_service_);
+    register_service(&builder, &async_service_);
+
     for (int i = 0; i < config.async_server_threads(); i++) {
       srv_cqs_.emplace_back(builder.AddCompletionQueue());
     }
@@ -75,22 +92,29 @@
     server_ = builder.BuildAndStart();
 
     using namespace std::placeholders;
+
+    auto process_rpc_bound =
+        std::bind(process_rpc, config.payload_config(), _1, _2);
+
     for (int i = 0; i < 10000 / config.async_server_threads(); i++) {
       for (int j = 0; j < config.async_server_threads(); j++) {
-        auto request_unary = std::bind(
-            &BenchmarkService::AsyncService::RequestUnaryCall, &async_service_,
-            _1, _2, _3, srv_cqs_[j].get(), srv_cqs_[j].get(), _4);
-        auto request_streaming = std::bind(
-            &BenchmarkService::AsyncService::RequestStreamingCall,
-            &async_service_, _1, _2, srv_cqs_[j].get(), srv_cqs_[j].get(), _3);
-        contexts_.push_front(
-            new ServerRpcContextUnaryImpl<SimpleRequest, SimpleResponse>(
-                request_unary, ProcessRPC));
-        contexts_.push_front(
-            new ServerRpcContextStreamingImpl<SimpleRequest, SimpleResponse>(
-                request_streaming, ProcessRPC));
+        if (request_unary_function) {
+          auto request_unary =
+              std::bind(request_unary_function, &async_service_, _1, _2, _3,
+                        srv_cqs_[j].get(), srv_cqs_[j].get(), _4);
+          contexts_.push_front(
+              new ServerRpcContextUnaryImpl(request_unary, process_rpc_bound));
+        }
+        if (request_streaming_function) {
+          auto request_streaming =
+              std::bind(request_streaming_function, &async_service_, _1, _2,
+                        srv_cqs_[j].get(), srv_cqs_[j].get(), _3);
+          contexts_.push_front(new ServerRpcContextStreamingImpl(
+              request_streaming, process_rpc_bound));
+        }
       }
     }
+
     for (int i = 0; i < config.async_server_threads(); i++) {
       shutdown_state_.emplace_back(new PerThreadShutdownState());
     }
@@ -155,16 +179,15 @@
     return reinterpret_cast<ServerRpcContext *>(tag);
   }
 
-  template <class RequestType, class ResponseType>
   class ServerRpcContextUnaryImpl GRPC_FINAL : public ServerRpcContext {
    public:
     ServerRpcContextUnaryImpl(
-        std::function<void(ServerContext *, RequestType *,
+        std::function<void(ServerContextType *, RequestType *,
                            grpc::ServerAsyncResponseWriter<ResponseType> *,
                            void *)> request_method,
         std::function<grpc::Status(const RequestType *, ResponseType *)>
             invoke_method)
-        : srv_ctx_(new ServerContext),
+        : srv_ctx_(new ServerContextType),
           next_state_(&ServerRpcContextUnaryImpl::invoker),
           request_method_(request_method),
           invoke_method_(invoke_method),
@@ -177,7 +200,7 @@
       return (this->*next_state_)(ok);
     }
     void Reset() GRPC_OVERRIDE {
-      srv_ctx_.reset(new ServerContext);
+      srv_ctx_.reset(new ServerContextType);
       req_ = RequestType();
       response_writer_ =
           grpc::ServerAsyncResponseWriter<ResponseType>(srv_ctx_.get());
@@ -205,10 +228,10 @@
       response_writer_.Finish(response, status, AsyncQpsServerTest::tag(this));
       return true;
     }
-    std::unique_ptr<ServerContext> srv_ctx_;
+    std::unique_ptr<ServerContextType> srv_ctx_;
     RequestType req_;
     bool (ServerRpcContextUnaryImpl::*next_state_)(bool);
-    std::function<void(ServerContext *, RequestType *,
+    std::function<void(ServerContextType *, RequestType *,
                        grpc::ServerAsyncResponseWriter<ResponseType> *, void *)>
         request_method_;
     std::function<grpc::Status(const RequestType *, ResponseType *)>
@@ -216,16 +239,16 @@
     grpc::ServerAsyncResponseWriter<ResponseType> response_writer_;
   };
 
-  template <class RequestType, class ResponseType>
   class ServerRpcContextStreamingImpl GRPC_FINAL : public ServerRpcContext {
    public:
     ServerRpcContextStreamingImpl(
-        std::function<void(ServerContext *, grpc::ServerAsyncReaderWriter<
-                                                ResponseType, RequestType> *,
-                           void *)> request_method,
+        std::function<void(
+            ServerContextType *,
+            grpc::ServerAsyncReaderWriter<ResponseType, RequestType> *, void *)>
+            request_method,
         std::function<grpc::Status(const RequestType *, ResponseType *)>
             invoke_method)
-        : srv_ctx_(new ServerContext),
+        : srv_ctx_(new ServerContextType),
           next_state_(&ServerRpcContextStreamingImpl::request_done),
           request_method_(request_method),
           invoke_method_(invoke_method),
@@ -237,7 +260,7 @@
       return (this->*next_state_)(ok);
     }
     void Reset() GRPC_OVERRIDE {
-      srv_ctx_.reset(new ServerContext);
+      srv_ctx_.reset(new ServerContextType);
       req_ = RequestType();
       stream_ = grpc::ServerAsyncReaderWriter<ResponseType, RequestType>(
           srv_ctx_.get());
@@ -286,11 +309,11 @@
     }
     bool finish_done(bool ok) { return false; /* reset the context */ }
 
-    std::unique_ptr<ServerContext> srv_ctx_;
+    std::unique_ptr<ServerContextType> srv_ctx_;
     RequestType req_;
     bool (ServerRpcContextStreamingImpl::*next_state_)(bool);
     std::function<void(
-        ServerContext *,
+        ServerContextType *,
         grpc::ServerAsyncReaderWriter<ResponseType, RequestType> *, void *)>
         request_method_;
     std::function<grpc::Status(const RequestType *, ResponseType *)>
@@ -298,20 +321,10 @@
     grpc::ServerAsyncReaderWriter<ResponseType, RequestType> stream_;
   };
 
-  static Status ProcessRPC(const SimpleRequest *request,
-                           SimpleResponse *response) {
-    if (request->response_size() > 0) {
-      if (!SetPayload(request->response_type(), request->response_size(),
-                      response->mutable_payload())) {
-        return Status(grpc::StatusCode::INTERNAL, "Error creating payload.");
-      }
-    }
-    return Status::OK;
-  }
   std::vector<std::thread> threads_;
   std::unique_ptr<grpc::Server> server_;
   std::vector<std::unique_ptr<grpc::ServerCompletionQueue>> srv_cqs_;
-  BenchmarkService::AsyncService async_service_;
+  ServiceType async_service_;
   std::forward_list<ServerRpcContext *> contexts_;
 
   class PerThreadShutdownState {
@@ -335,8 +348,52 @@
   std::vector<std::unique_ptr<PerThreadShutdownState>> shutdown_state_;
 };
 
+static void RegisterBenchmarkService(ServerBuilder *builder,
+                                     BenchmarkService::AsyncService *service) {
+  builder->RegisterAsyncService(service);
+}
+static void RegisterGenericService(ServerBuilder *builder,
+                                   grpc::AsyncGenericService *service) {
+  builder->RegisterAsyncGenericService(service);
+}
+
+static Status ProcessSimpleRPC(const PayloadConfig &,
+                               const SimpleRequest *request,
+                               SimpleResponse *response) {
+  if (request->response_size() > 0) {
+    if (!Server::SetPayload(request->response_type(), request->response_size(),
+                            response->mutable_payload())) {
+      return Status(grpc::StatusCode::INTERNAL, "Error creating payload.");
+    }
+  }
+  return Status::OK;
+}
+
+static Status ProcessGenericRPC(const PayloadConfig &payload_config,
+                                const ByteBuffer *request,
+                                ByteBuffer *response) {
+  int resp_size = payload_config.bytebuf_params().resp_size();
+  std::unique_ptr<char> buf(new char[resp_size]);
+  gpr_slice s = gpr_slice_from_copied_buffer(buf.get(), resp_size);
+  Slice slice(s, Slice::STEAL_REF);
+  *response = ByteBuffer(&slice, 1);
+  return Status::OK;
+}
+
 std::unique_ptr<Server> CreateAsyncServer(const ServerConfig &config) {
-  return std::unique_ptr<Server>(new AsyncQpsServerTest(config));
+  return std::unique_ptr<Server>(new AsyncQpsServerTest<
+      SimpleRequest, SimpleResponse, BenchmarkService::AsyncService,
+      grpc::ServerContext>(
+      config, RegisterBenchmarkService,
+      &BenchmarkService::AsyncService::RequestUnaryCall,
+      &BenchmarkService::AsyncService::RequestStreamingCall, ProcessSimpleRPC));
+}
+std::unique_ptr<Server> CreateAsyncGenericServer(const ServerConfig &config) {
+  return std::unique_ptr<Server>(
+      new AsyncQpsServerTest<ByteBuffer, ByteBuffer, grpc::AsyncGenericService,
+                             grpc::GenericServerContext>(
+          config, RegisterGenericService, nullptr,
+          &grpc::AsyncGenericService::RequestCall, ProcessGenericRPC));
 }
 
 }  // namespace testing
diff --git a/test/cpp/util/cli_call.cc b/test/cpp/util/cli_call.cc
index 0de388d..5e29e7a 100644
--- a/test/cpp/util/cli_call.cc
+++ b/test/cpp/util/cli_call.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -35,13 +35,13 @@
 
 #include <iostream>
 
-#include <grpc/grpc.h>
-#include <grpc/support/log.h>
-#include <grpc/support/slice.h>
-#include <grpc++/support/byte_buffer.h>
 #include <grpc++/channel.h>
 #include <grpc++/client_context.h>
 #include <grpc++/generic/generic_stub.h>
+#include <grpc++/support/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/slice.h>
 
 namespace grpc {
 namespace testing {
diff --git a/test/cpp/util/cli_call_test.cc b/test/cpp/util/cli_call_test.cc
index 94f5461..5fdf519 100644
--- a/test/cpp/util/cli_call_test.cc
+++ b/test/cpp/util/cli_call_test.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -53,7 +53,7 @@
 namespace grpc {
 namespace testing {
 
-class TestServiceImpl : public ::grpc::testing::TestService::Service {
+class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
  public:
   Status Echo(ServerContext* context, const EchoRequest* request,
               EchoResponse* response) GRPC_OVERRIDE {
@@ -91,11 +91,11 @@
   void ResetStub() {
     channel_ =
         CreateChannel(server_address_.str(), InsecureChannelCredentials());
-    stub_ = grpc::testing::TestService::NewStub(channel_);
+    stub_ = grpc::testing::EchoTestService::NewStub(channel_);
   }
 
   std::shared_ptr<Channel> channel_;
-  std::unique_ptr<grpc::testing::TestService::Stub> stub_;
+  std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
   std::unique_ptr<Server> server_;
   std::ostringstream server_address_;
   TestServiceImpl service_;
@@ -115,7 +115,7 @@
   EXPECT_EQ(response.message(), request.message());
   EXPECT_TRUE(s.ok());
 
-  const grpc::string kMethod("/grpc.testing.TestService/Echo");
+  const grpc::string kMethod("/grpc.testing.EchoTestService/Echo");
   grpc::string request_bin, response_bin, expected_response_bin;
   EXPECT_TRUE(request.SerializeToString(&request_bin));
   EXPECT_TRUE(response.SerializeToString(&expected_response_bin));
diff --git a/tools/buildgen/plugins/transitive_dependencies.py b/tools/buildgen/plugins/transitive_dependencies.py
index c2d3da3..01e7f61 100644
--- a/tools/buildgen/plugins/transitive_dependencies.py
+++ b/tools/buildgen/plugins/transitive_dependencies.py
@@ -1,4 +1,4 @@
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -36,10 +36,13 @@
 """
 
 def get_lib(libs, name):
-  return next(lib for lib in libs if lib['name']==name)
+  try:
+    return next(lib for lib in libs if lib['name']==name)
+  except StopIteration:
+    return None
 
 def transitive_deps(lib, libs):
-  if 'deps' in lib:
+  if lib is not None and 'deps' in lib:
     # Recursively call transitive_deps on each dependency, and take the union
     return set.union(set(lib['deps']),
                      *[set(transitive_deps(get_lib(libs, dep), libs))
@@ -58,6 +61,10 @@
   node_modules = dictionary.get('node_modules')
   targets = dictionary.get('targets')
 
-  for target_list in (libs, node_modules, targets):
+  for target_list in (libs, targets, node_modules):
     for target in target_list:
       target['transitive_deps'] = transitive_deps(target, libs)
+
+  python_dependencies = dictionary.get('python_dependencies')
+  python_dependencies['transitive_deps'] = (
+      transitive_deps(python_dependencies, libs))
diff --git a/tools/distrib/check_copyright.py b/tools/distrib/check_copyright.py
index f54e5fa..0c06690 100755
--- a/tools/distrib/check_copyright.py
+++ b/tools/distrib/check_copyright.py
@@ -54,6 +54,9 @@
                   default=0,
                   action='store_const',
                   const=1)
+argp.add_argument('-f', '--fix',
+                  default=False,
+                  action='store_true');
 args = argp.parse_args()
 
 # open the license text
@@ -90,7 +93,7 @@
 ])
 
 
-RE_YEAR = r'Copyright (?:[0-9]+\-)?([0-9]+), Google Inc\.'
+RE_YEAR = r'Copyright (?P<first_year>[0-9]+\-)?(?P<last_year>[0-9]+), Google Inc\.'
 RE_LICENSE = dict(
     (k, r'\n'.join(
         LICENSE_PREFIX[k] +
@@ -101,8 +104,11 @@
 
 def load(name):
   with open(name) as f:
-    return '\n'.join(line.rstrip() for line in f.read().splitlines())
+    return f.read()
 
+def save(name, text):
+  with open(name, 'w') as f:
+    f.write(text)
 
 assert(re.search(RE_LICENSE['LICENSE'], load('LICENSE')))
 assert(re.search(RE_LICENSE['Makefile'], load('Makefile')))
@@ -117,6 +123,7 @@
 
 
 # scan files, validate the text
+ok = True
 for filename in subprocess.check_output('git ls-tree -r --name-only -r HEAD',
                                         shell=True).splitlines():
   if filename in KNOWN_BAD: continue
@@ -130,17 +137,27 @@
     log(args.skips, 'skip', filename)
     continue
   text = load(filename)
-  ok = True
   m = re.search(re_license, text)
   if m:
+    gdict = m.groupdict()
     last_modified = int(subprocess.check_output('git log -1 --format="%ad" --date=short -- ' + filename, shell=True)[0:4])
-    latest_claimed = int(m.group(1))
+    latest_claimed = int(gdict['last_year'])
     if last_modified > latest_claimed:
       print '%s modified %d but copyright only extends to %d' % (filename, last_modified, latest_claimed)
       ok = False
+      if args.fix:
+        span_start, span_end = m.span(2)
+        if not gdict['first_year']:
+          # prepend the old year to the current one.
+          text = '{}-{}{}'.format(text[:span_end], last_modified, text[span_end:])
+        else:  # already a year range
+          # simply update the last year
+          text = '{}{}{}'.format(text[:span_start], last_modified, text[span_end:])
+        save(filename, text)
+        print 'Fixed!'
+        ok = True
   elif 'DO NOT EDIT' not in text and 'AssemblyInfo.cs' not in filename and filename != 'src/boringssl/err_data.c':
     log(1, 'copyright missing', filename)
     ok = False
 
 sys.exit(0 if ok else 1)
-
diff --git a/tools/distrib/clang_format_code.sh b/tools/distrib/clang_format_code.sh
index 612074a..6bfa278 100755
--- a/tools/distrib/clang_format_code.sh
+++ b/tools/distrib/clang_format_code.sh
@@ -38,4 +38,3 @@
 
 # run clang-format against the checked out codebase
 docker run -e TEST=$TEST --rm=true -v ${HOST_GIT_ROOT:-`pwd`}:/local-code -t grpc_clang_format /clang_format_all_the_things.sh
-
diff --git a/tools/distrib/python/submit.py b/tools/distrib/python/submit.py
index dffbefd..9b012be 100755
--- a/tools/distrib/python/submit.py
+++ b/tools/distrib/python/submit.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python2.7
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -55,11 +55,19 @@
     help='Password to authenticate with the repository. Not needed if you have '
          'configured your .pypirc to include your password.'
 )
+parser.add_argument(
+    '--bdist', '-b', action='store_true',
+    help='Generate a binary distribution (wheel) for the current OS.'
+)
+parser.add_argument(
+    '--dist-args', type=str,
+    help='Additional arguments to pass to the *dist setup.py command.'
+)
 args = parser.parse_args()
 
 # Move to the root directory of Python GRPC.
 pkgdir = os.path.join(os.path.dirname(os.path.abspath(__file__)),
-                      '../../../src/python/grpcio')
+                      '../../../')
 # Remove previous distributions; they somehow confuse twine.
 try:
   shutil.rmtree(os.path.join(pkgdir, 'dist/'))
@@ -73,7 +81,12 @@
 subprocess.call(cmd, cwd=pkgdir, env=build_env)
 
 # Make the push.
-cmd = ['python', 'setup.py', 'sdist']
+if args.bdist:
+  cmd = ['python', 'setup.py', 'bdist_wheel']
+else:
+  cmd = ['python', 'setup.py', 'sdist']
+if args.dist_args:
+  cmd += args.dist_args.split()
 subprocess.call(cmd, cwd=pkgdir)
 
 cmd = ['twine', 'upload', '-r', args.repository]
diff --git a/tools/dockerfile/grpc_clang_format/clang_format_all_the_things.sh b/tools/dockerfile/grpc_clang_format/clang_format_all_the_things.sh
index ededc6e..87445c7 100755
--- a/tools/dockerfile/grpc_clang_format/clang_format_all_the_things.sh
+++ b/tools/dockerfile/grpc_clang_format/clang_format_all_the_things.sh
@@ -65,4 +65,3 @@
     false
   fi
 fi
-
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index aef5bec..2280fde 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -1014,6 +1014,7 @@
 src/core/surface/server.c \
 src/core/surface/server_chttp2.c \
 src/core/surface/server_create.c \
+src/core/surface/validate_metadata.c \
 src/core/surface/version.c \
 src/core/transport/byte_stream.c \
 src/core/transport/chttp2/alpn.c \
diff --git a/tools/jenkins/build_interop_stress_image.sh b/tools/jenkins/build_interop_stress_image.sh
new file mode 100755
index 0000000..6b22dce
--- /dev/null
+++ b/tools/jenkins/build_interop_stress_image.sh
@@ -0,0 +1,86 @@
+#!/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.
+#
+# This script is invoked by run_interop_tests.py to build the docker image
+# for interop testing. You should never need to call this script on your own.
+
+set -x
+
+# Params:
+#  INTEROP_IMAGE - name of tag of the final interop image
+#  BASE_NAME - base name used to locate the base Dockerfile and build script
+#  TTY_FLAG - optional -t flag to make docker allocate tty
+#  BUILD_INTEROP_DOCKER_EXTRA_ARGS - optional args to be passed to the
+#    docker run command
+
+cd `dirname $0`/../..
+GRPC_ROOT=`pwd`
+MOUNT_ARGS="-v $GRPC_ROOT:/var/local/jenkins/grpc:ro"
+
+mkdir -p /tmp/ccache
+
+# Mount service account dir if available.
+# If service_directory does not contain the service account JSON file,
+# some of the tests will fail.
+if [ -e $HOME/service_account ]
+then
+  MOUNT_ARGS+=" -v $HOME/service_account:/var/local/jenkins/service_account:ro"
+fi
+
+# Use image name based on Dockerfile checksum
+BASE_IMAGE=${BASE_NAME}_base:`sha1sum tools/jenkins/$BASE_NAME/Dockerfile | cut -f1 -d\ `
+
+# Make sure base docker image has been built. Should be instantaneous if so.
+docker build -t $BASE_IMAGE --force-rm=true tools/jenkins/$BASE_NAME || exit $?
+
+# Create a local branch so the child Docker script won't complain
+git branch -f jenkins-docker
+
+CONTAINER_NAME="build_${BASE_NAME}_$(uuidgen)"
+
+# Prepare image for interop tests, commit it on success.
+(docker run \
+  -e CCACHE_DIR=/tmp/ccache \
+  -e THIS_IS_REALLY_NEEDED='see https://github.com/docker/docker/issues/14203 for why docker is awful' \
+  -i $TTY_FLAG \
+  $MOUNT_ARGS \
+  $BUILD_INTEROP_DOCKER_EXTRA_ARGS \
+  -v /tmp/ccache:/tmp/ccache \
+  --name=$CONTAINER_NAME \
+  $BASE_IMAGE \
+  bash -l /var/local/jenkins/grpc/tools/jenkins/$BASE_NAME/build_interop_stress.sh \
+  && docker commit $CONTAINER_NAME $INTEROP_IMAGE \
+  && echo "Successfully built image $INTEROP_IMAGE")
+EXITCODE=$?
+
+# remove intermediate container, possibly killing it first
+docker rm -f $CONTAINER_NAME
+
+exit $EXITCODE
diff --git a/tools/jenkins/grpc_interop_python/Dockerfile b/tools/jenkins/grpc_interop_python/Dockerfile
index 6034cbf..047604b 100644
--- a/tools/jenkins/grpc_interop_python/Dockerfile
+++ b/tools/jenkins/grpc_interop_python/Dockerfile
@@ -48,6 +48,7 @@
   libc6-dbg \
   libc6-dev \
   libgtest-dev \
+  libssl-dev \
   libtool \
   make \
   strace \
diff --git a/tools/jenkins/grpc_interop_python/build_interop.sh b/tools/jenkins/grpc_interop_python/build_interop.sh
index 8f5bfd1..39c9367 100755
--- a/tools/jenkins/grpc_interop_python/build_interop.sh
+++ b/tools/jenkins/grpc_interop_python/build_interop.sh
@@ -43,5 +43,5 @@
 make
 
 # build Python interop client and server
-CONFIG=opt ./tools/run_tests/build_python.sh 2.7
+CONFIG=opt ./tools/run_tests/build_python.sh
 
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd b/tools/jenkins/grpc_interop_stress_cxx/Dockerfile
similarity index 61%
copy from src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd
copy to tools/jenkins/grpc_interop_stress_cxx/Dockerfile
index 3e341bf..1fa1907 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd
+++ b/tools/jenkins/grpc_interop_stress_cxx/Dockerfile
@@ -27,10 +27,49 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-from grpc._cython._cygrpc cimport grpc
+# A work-in-progress Dockerfile that allows running gRPC test suites
+# inside a docker container.
 
+FROM debian:jessie
 
-cdef class Channel:
+# Install Git.
+RUN apt-get update && apt-get install -y \
+  autoconf \
+  autotools-dev \
+  build-essential \
+  bzip2 \
+  ccache \
+  curl \
+  gcc \
+  gcc-multilib \
+  git \
+  gyp \
+  libc6 \
+  libc6-dbg \
+  libc6-dev \
+  libgtest-dev \
+  libtool \
+  make \
+  strace \
+  python-dev \
+  python-setuptools \
+  python-yaml \
+  telnet \
+  unzip \
+  wget \
+  zip && apt-get clean
 
-  cdef grpc.grpc_channel *c_channel
-  cdef list references
+# Prepare ccache
+RUN ln -s /usr/bin/ccache /usr/local/bin/gcc
+RUN ln -s /usr/bin/ccache /usr/local/bin/g++
+RUN ln -s /usr/bin/ccache /usr/local/bin/cc
+RUN ln -s /usr/bin/ccache /usr/local/bin/c++
+RUN ln -s /usr/bin/ccache /usr/local/bin/clang
+RUN ln -s /usr/bin/ccache /usr/local/bin/clang++
+
+##################
+# C++ dependencies
+RUN apt-get update && apt-get -y install libgflags-dev libgtest-dev libc++-dev clang
+
+# Define the default command.
+CMD ["bash"]
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd b/tools/jenkins/grpc_interop_stress_cxx/build_interop_stress.sh
old mode 100644
new mode 100755
similarity index 77%
copy from src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd
copy to tools/jenkins/grpc_interop_stress_cxx/build_interop_stress.sh
index 3e341bf..01f9a9c
--- a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd
+++ b/tools/jenkins/grpc_interop_stress_cxx/build_interop_stress.sh
@@ -1,3 +1,4 @@
+#!/bin/bash
 # Copyright 2015, Google Inc.
 # All rights reserved.
 #
@@ -26,11 +27,19 @@
 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Builds C++ interop server and client in a base image.
+set -e
 
-from grpc._cython._cygrpc cimport grpc
+mkdir -p /var/local/git
+git clone --recursive /var/local/jenkins/grpc /var/local/git/grpc
 
+# copy service account keys if available
+cp -r /var/local/jenkins/service_account $HOME || true
 
-cdef class Channel:
+cd /var/local/git/grpc
 
-  cdef grpc.grpc_channel *c_channel
-  cdef list references
+make install-certs
+
+# build C++ interop stress client, interop client and server
+make stress_test interop_client interop_server
diff --git a/tools/jenkins/grpc_jenkins_slave/Dockerfile b/tools/jenkins/grpc_jenkins_slave/Dockerfile
index f3bf6bc..b1ac024 100644
--- a/tools/jenkins/grpc_jenkins_slave/Dockerfile
+++ b/tools/jenkins/grpc_jenkins_slave/Dockerfile
@@ -1,4 +1,4 @@
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
diff --git a/tools/jenkins/grpc_jenkins_slave_32bits/Dockerfile b/tools/jenkins/grpc_jenkins_slave_32bits/Dockerfile
index 1a86c5a..348a333 100644
--- a/tools/jenkins/grpc_jenkins_slave_32bits/Dockerfile
+++ b/tools/jenkins/grpc_jenkins_slave_32bits/Dockerfile
@@ -1,4 +1,4 @@
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd b/tools/jenkins/run_interop_stress.sh
old mode 100644
new mode 100755
similarity index 87%
copy from src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd
copy to tools/jenkins/run_interop_stress.sh
index 3e341bf..22d81db
--- a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd
+++ b/tools/jenkins/run_interop_stress.sh
@@ -1,3 +1,4 @@
+#!/usr/bin/env bash
 # Copyright 2015, Google Inc.
 # All rights reserved.
 #
@@ -26,11 +27,11 @@
 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# This script is invoked by Jenkins and runs interop test suite.
+set -ex
 
-from grpc._cython._cygrpc cimport grpc
+# Enter the gRPC repo root
+cd $(dirname $0)/../..
 
-
-cdef class Channel:
-
-  cdef grpc.grpc_channel *c_channel
-  cdef list references
+tools/run_tests/run_stress_tests.py -l all -s all -j 12 $@ || true
diff --git a/tools/jenkins/run_jenkins.sh b/tools/jenkins/run_jenkins.sh
index 9b6ba71..84b4ea5 100755
--- a/tools/jenkins/run_jenkins.sh
+++ b/tools/jenkins/run_jenkins.sh
@@ -1,5 +1,5 @@
 #!/usr/bin/env bash
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -92,4 +92,3 @@
 then
   exit 1
 fi
-
diff --git a/tools/run_tests/build_python.sh b/tools/run_tests/build_python.sh
index 57080ce..e0fcbb6 100755
--- a/tools/run_tests/build_python.sh
+++ b/tools/run_tests/build_python.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -34,16 +34,14 @@
 cd $(dirname $0)/../..
 
 ROOT=`pwd`
-GRPCIO=$ROOT/src/python/grpcio
 export LD_LIBRARY_PATH=$ROOT/libs/$CONFIG
 export DYLD_LIBRARY_PATH=$ROOT/libs/$CONFIG
 export PATH=$ROOT/bins/$CONFIG:$ROOT/bins/$CONFIG/protobuf:$PATH
-export CFLAGS="-I$ROOT/include -std=c89"
+export CFLAGS="-I$ROOT/include -std=gnu99"
 export LDFLAGS="-L$ROOT/libs/$CONFIG"
 export GRPC_PYTHON_BUILD_WITH_CYTHON=1
 export GRPC_PYTHON_ENABLE_CYTHON_TRACING=1
 
-cd $GRPCIO
 tox --notest
 
-$GRPCIO/.tox/py27/bin/python $GRPCIO/setup.py build
+$ROOT/.tox/py27/bin/python $ROOT/setup.py build
diff --git a/tools/run_tests/build_ruby.sh b/tools/run_tests/build_ruby.sh
index 6d23c31..8acb40d 100755
--- a/tools/run_tests/build_ruby.sh
+++ b/tools/run_tests/build_ruby.sh
@@ -34,7 +34,7 @@
 export GRPC_CONFIG=${CONFIG:-opt}
 
 # change to grpc's ruby directory
-cd $(dirname $0)/../../src/ruby
+cd $(dirname $0)/../..
 
 rm -rf ./tmp
 rake compile:grpc
diff --git a/tools/run_tests/jobset.py b/tools/run_tests/jobset.py
index 0b01bc4..e33433d 100755
--- a/tools/run_tests/jobset.py
+++ b/tools/run_tests/jobset.py
@@ -1,4 +1,4 @@
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -450,4 +450,3 @@
       js.set_remaining(remaining)
   js.finish()
   return js.get_num_failures(), js.resultset
-
diff --git a/tools/run_tests/run_interop_tests.py b/tools/run_tests/run_interop_tests.py
index e69e987..10566d6 100755
--- a/tools/run_tests/run_interop_tests.py
+++ b/tools/run_tests/run_interop_tests.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -298,11 +298,8 @@
 
   def client_cmd(self, args):
     return [
-        'src/python/grpcio/.tox/py27/bin/python',
-        'src/python/grpcio/setup.py',
-        'run_interop',
-        '--client',
-        '--args=\'{}\''.format(' '.join(args))
+        'tox -einterop_client --',
+        ' '.join(args)
     ]
 
   def cloud_to_prod_env(self):
@@ -310,11 +307,8 @@
 
   def server_cmd(self, args):
     return [
-        'src/python/grpcio/.tox/py27/bin/python',
-        'src/python/grpcio/setup.py',
-        'run_interop',
-        '--server',
-        '--args=\'{}\''.format(' '.join(args) + ' --use_tls=true')
+        'tox -einterop_server --',
+        ' '.join(args) + ' --use_tls=true'
     ]
 
   def global_env(self):
@@ -555,7 +549,7 @@
   match = re.search(r'\{"cases[^\]]*\]\}', stdout)
   if not match:
     return None
-    
+
   results = json.loads(match.group(0))
   skipped = 0
   passed = 0
@@ -748,7 +742,7 @@
       for test_case in _HTTP2_TEST_CASES:
         if server_name == "go":
           # TODO(carl-mastrangelo): Reenable after https://github.com/grpc/grpc-go/issues/434
-          continue 
+          continue
         test_job = cloud_to_cloud_jobspec(http2Interop,
                                           test_case,
                                           server_name,
@@ -777,7 +771,7 @@
       job[0].http2results = aggregate_http2_results(job[0].message)
 
   report_utils.render_interop_html_report(
-      set([str(l) for l in languages]), servers, _TEST_CASES, _AUTH_TEST_CASES, 
+      set([str(l) for l in languages]), servers, _TEST_CASES, _AUTH_TEST_CASES,
       _HTTP2_TEST_CASES, resultset, num_failures,
       args.cloud_to_prod_auth or args.cloud_to_prod, args.http2_interop)
 
diff --git a/tools/run_tests/run_python.sh b/tools/run_tests/run_python.sh
index 042b404..ffe9c12 100755
--- a/tools/run_tests/run_python.sh
+++ b/tools/run_tests/run_python.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -34,7 +34,6 @@
 cd $(dirname $0)/../..
 
 ROOT=`pwd`
-GRPCIO=$ROOT/src/python/grpcio
 export LD_LIBRARY_PATH=$ROOT/libs/$CONFIG
 export DYLD_LIBRARY_PATH=$ROOT/libs/$CONFIG
 export PATH=$ROOT/bins/$CONFIG:$ROOT/bins/$CONFIG/protobuf:$PATH
@@ -43,9 +42,8 @@
 export GRPC_PYTHON_BUILD_WITH_CYTHON=1
 export GRPC_PYTHON_ENABLE_CYTHON_TRACING=1
 
-cd $GRPCIO
 tox
 
 mkdir -p $ROOT/reports
 rm -rf $ROOT/reports/python-coverage
-(mv -T $GRPCIO/htmlcov $ROOT/reports/python-coverage) || true
+(mv -T $ROOT/htmlcov $ROOT/reports/python-coverage) || true
diff --git a/tools/run_tests/run_ruby.sh b/tools/run_tests/run_ruby.sh
index b82ce52..73a84ac 100755
--- a/tools/run_tests/run_ruby.sh
+++ b/tools/run_tests/run_ruby.sh
@@ -31,6 +31,6 @@
 set -ex
 
 # change to grpc repo root
-cd $(dirname $0)/../../src/ruby
+cd $(dirname $0)/../..
 
 rake
diff --git a/tools/run_tests/run_stress_tests.py b/tools/run_tests/run_stress_tests.py
new file mode 100755
index 0000000..b01a07a
--- /dev/null
+++ b/tools/run_tests/run_stress_tests.py
@@ -0,0 +1,328 @@
+#!/usr/bin/env python
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Run stress test in C++"""
+
+import argparse
+import atexit
+import dockerjob
+import itertools
+import jobset
+import json
+import multiprocessing
+import os
+import re
+import subprocess
+import sys
+import tempfile
+import time
+import uuid
+
+# Docker doesn't clean up after itself, so we do it on exit.
+atexit.register(lambda: subprocess.call(['stty', 'echo']))
+
+ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..'))
+os.chdir(ROOT)
+
+_DEFAULT_SERVER_PORT = 8080
+_DEFAULT_METRICS_PORT = 8081
+_DEFAULT_TEST_CASES = 'empty_unary:20,large_unary:20,client_streaming:20,server_streaming:20,empty_stream:20'
+_DEFAULT_NUM_CHANNELS_PER_SERVER = 5
+_DEFAULT_NUM_STUBS_PER_CHANNEL = 10
+
+# 15 mins default
+_DEFAULT_TEST_DURATION_SECS = 900
+
+class CXXLanguage:
+
+  def __init__(self):
+    self.client_cwd = None
+    self.server_cwd = None
+    self.safename = 'cxx'
+
+  def client_cmd(self, args):
+    return ['bins/opt/stress_test'] + args
+
+  def server_cmd(self, args):
+    return ['bins/opt/interop_server'] + args
+
+  def global_env(self):
+    return {}
+
+  def __str__(self):
+    return 'c++'
+
+
+_LANGUAGES = {'c++': CXXLanguage(),}
+
+# languages supported as cloud_to_cloud servers
+_SERVERS = ['c++']
+
+DOCKER_WORKDIR_ROOT = '/var/local/git/grpc'
+
+
+def docker_run_cmdline(cmdline, image, docker_args=[], cwd=None, environ=None):
+  """Wraps given cmdline array to create 'docker run' cmdline from it."""
+  docker_cmdline = ['docker', 'run', '-i', '--rm=true']
+
+  # turn environ into -e docker args
+  if environ:
+    for k, v in environ.iteritems():
+      docker_cmdline += ['-e', '%s=%s' % (k, v)]
+
+  # set working directory
+  workdir = DOCKER_WORKDIR_ROOT
+  if cwd:
+    workdir = os.path.join(workdir, cwd)
+  docker_cmdline += ['-w', workdir]
+
+  docker_cmdline += docker_args + [image] + cmdline
+  return docker_cmdline
+
+
+def bash_login_cmdline(cmdline):
+  """Creates bash -l -c cmdline from args list."""
+  # Use login shell:
+  # * rvm and nvm require it
+  # * makes error messages clearer if executables are missing
+  return ['bash', '-l', '-c', ' '.join(cmdline)]
+
+
+def _job_kill_handler(job):
+  if job._spec.container_name:
+    dockerjob.docker_kill(job._spec.container_name)
+    # When the job times out and we decide to kill it,
+    # we need to wait a before restarting the job
+    # to prevent "container name already in use" error.
+    # TODO(jtattermusch): figure out a cleaner way to to this.
+    time.sleep(2)
+
+
+def cloud_to_cloud_jobspec(language,
+                           test_cases,
+                           server_addresses,
+                           test_duration_secs,
+                           num_channels_per_server,
+                           num_stubs_per_channel,
+                           metrics_port,
+                           docker_image=None):
+  """Creates jobspec for cloud-to-cloud interop test"""
+  cmdline = bash_login_cmdline(language.client_cmd([
+      '--test_cases=%s' % test_cases, '--server_addresses=%s' %
+      server_addresses, '--test_duration_secs=%s' % test_duration_secs,
+      '--num_stubs_per_channel=%s' % num_stubs_per_channel,
+      '--num_channels_per_server=%s' % num_channels_per_server,
+      '--metrics_port=%s' % metrics_port
+  ]))
+  print cmdline
+  cwd = language.client_cwd
+  environ = language.global_env()
+  if docker_image:
+    container_name = dockerjob.random_name('interop_client_%s' %
+                                           language.safename)
+    cmdline = docker_run_cmdline(
+        cmdline,
+        image=docker_image,
+        environ=environ,
+        cwd=cwd,
+        docker_args=['--net=host', '--name', container_name])
+    cwd = None
+
+  test_job = jobset.JobSpec(cmdline=cmdline,
+                            cwd=cwd,
+                            environ=environ,
+                            shortname='cloud_to_cloud:%s:%s_server:stress_test' % (
+                                language, server_name),
+                            timeout_seconds=test_duration_secs * 2,
+                            flake_retries=0,
+                            timeout_retries=0,
+                            kill_handler=_job_kill_handler)
+  test_job.container_name = container_name
+  return test_job
+
+
+def server_jobspec(language, docker_image, test_duration_secs):
+  """Create jobspec for running a server"""
+  container_name = dockerjob.random_name('interop_server_%s' %
+                                         language.safename)
+  cmdline = bash_login_cmdline(language.server_cmd(['--port=%s' %
+                                                    _DEFAULT_SERVER_PORT]))
+  environ = language.global_env()
+  docker_cmdline = docker_run_cmdline(
+      cmdline,
+      image=docker_image,
+      cwd=language.server_cwd,
+      environ=environ,
+      docker_args=['-p', str(_DEFAULT_SERVER_PORT), '--name', container_name])
+
+  server_job = jobset.JobSpec(cmdline=docker_cmdline,
+                              environ=environ,
+                              shortname='interop_server_%s' % language,
+                              timeout_seconds=test_duration_secs * 3)
+  server_job.container_name = container_name
+  return server_job
+
+
+def build_interop_stress_image_jobspec(language, tag=None):
+  """Creates jobspec for building stress test docker image for a language"""
+  if not tag:
+    tag = 'grpc_interop_stress_%s:%s' % (language.safename, uuid.uuid4())
+  env = {'INTEROP_IMAGE': tag,
+         'BASE_NAME': 'grpc_interop_stress_%s' % language.safename}
+  build_job = jobset.JobSpec(cmdline=['tools/jenkins/build_interop_stress_image.sh'],
+                             environ=env,
+                             shortname='build_docker_%s' % (language),
+                             timeout_seconds=30 * 60)
+  build_job.tag = tag
+  return build_job
+
+argp = argparse.ArgumentParser(description='Run stress tests.')
+argp.add_argument('-l',
+                  '--language',
+                  choices=['all'] + sorted(_LANGUAGES),
+                  nargs='+',
+                  default=['all'],
+                  help='Clients to run.')
+argp.add_argument('-j', '--jobs', default=multiprocessing.cpu_count(), type=int)
+argp.add_argument(
+    '-s',
+    '--server',
+    choices=['all'] + sorted(_SERVERS),
+    action='append',
+    help='Run cloud_to_cloud servers in a separate docker ' + 'image.',
+    default=[])
+argp.add_argument(
+    '--override_server',
+    action='append',
+    type=lambda kv: kv.split('='),
+    help=
+    'Use servername=HOST:PORT to explicitly specify a server. E.g. '
+    'csharp=localhost:50000',
+    default=[])
+argp.add_argument('--test_duration_secs',
+                  help='The duration of the test in seconds',
+                  default=_DEFAULT_TEST_DURATION_SECS)
+
+args = argp.parse_args()
+
+servers = set(
+    s
+    for s in itertools.chain.from_iterable(_SERVERS if x == 'all' else [x]
+                                           for x in args.server))
+
+languages = set(_LANGUAGES[l]
+                for l in itertools.chain.from_iterable(_LANGUAGES.iterkeys(
+                ) if x == 'all' else [x] for x in args.language))
+
+docker_images = {}
+# languages for which to build docker images
+languages_to_build = set(
+    _LANGUAGES[k]
+    for k in set([str(l) for l in languages] + [s for s in servers]))
+build_jobs = []
+for l in languages_to_build:
+  job = build_interop_stress_image_jobspec(l)
+  docker_images[str(l)] = job.tag
+  build_jobs.append(job)
+
+if build_jobs:
+  jobset.message('START', 'Building interop docker images.', do_newline=True)
+  num_failures, _ = jobset.run(build_jobs,
+                               newline_on_success=True,
+                               maxjobs=args.jobs)
+  if num_failures == 0:
+    jobset.message('SUCCESS',
+                   'All docker images built successfully.',
+                   do_newline=True)
+  else:
+    jobset.message('FAILED',
+                   'Failed to build interop docker images.',
+                   do_newline=True)
+    for image in docker_images.itervalues():
+      dockerjob.remove_image(image, skip_nonexistent=True)
+    sys.exit(1)
+
+# Start interop servers.
+server_jobs = {}
+server_addresses = {}
+try:
+  for s in servers:
+    lang = str(s)
+    spec = server_jobspec(_LANGUAGES[lang], docker_images.get(lang), args.test_duration_secs)
+    job = dockerjob.DockerJob(spec)
+    server_jobs[lang] = job
+    server_addresses[lang] = ('localhost',
+                              job.mapped_port(_DEFAULT_SERVER_PORT))
+
+  jobs = []
+
+  for server in args.override_server:
+    server_name = server[0]
+    (server_host, server_port) = server[1].split(':')
+    server_addresses[server_name] = (server_host, server_port)
+
+  for server_name, server_address in server_addresses.iteritems():
+    (server_host, server_port) = server_address
+    for language in languages:
+      test_job = cloud_to_cloud_jobspec(
+          language,
+          _DEFAULT_TEST_CASES,
+          ('%s:%s' % (server_host, server_port)),
+          args.test_duration_secs,
+          _DEFAULT_NUM_CHANNELS_PER_SERVER,
+          _DEFAULT_NUM_STUBS_PER_CHANNEL,
+          _DEFAULT_METRICS_PORT,
+          docker_image=docker_images.get(str(language)))
+      jobs.append(test_job)
+
+  if not jobs:
+    print 'No jobs to run.'
+    for image in docker_images.itervalues():
+      dockerjob.remove_image(image, skip_nonexistent=True)
+    sys.exit(1)
+
+  num_failures, resultset = jobset.run(jobs,
+                                       newline_on_success=True,
+                                       maxjobs=args.jobs)
+  if num_failures:
+    jobset.message('FAILED', 'Some tests failed', do_newline=True)
+  else:
+    jobset.message('SUCCESS', 'All tests passed', do_newline=True)
+
+finally:
+  # Check if servers are still running.
+  for server, job in server_jobs.iteritems():
+    if not job.is_running():
+      print 'Server "%s" has exited prematurely.' % server
+
+  dockerjob.finish_jobs([j for j in server_jobs.itervalues()])
+
+  for image in docker_images.itervalues():
+    print 'Removing docker image %s' % image
+    dockerjob.remove_image(image)
diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json
index 8d86fa3..3e42c59 100644
--- a/tools/run_tests/sources_and_headers.json
+++ b/tools/run_tests/sources_and_headers.json
@@ -1504,6 +1504,23 @@
       "grpc", 
       "grpc++", 
       "grpc++_test_util", 
+      "grpc_test_util", 
+      "qps"
+    ], 
+    "headers": [], 
+    "language": "c++", 
+    "name": "generic_async_streaming_ping_pong_test", 
+    "src": [
+      "test/cpp/qps/generic_async_streaming_ping_pong_test.cc"
+    ]
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc++", 
+      "grpc++_test_util", 
       "grpc_test_util"
     ], 
     "headers": [], 
@@ -3212,6 +3229,7 @@
       "src/core/surface/server_chttp2.c", 
       "src/core/surface/server_create.c", 
       "src/core/surface/surface_trace.h", 
+      "src/core/surface/validate_metadata.c", 
       "src/core/surface/version.c", 
       "src/core/transport/byte_stream.c", 
       "src/core/transport/byte_stream.h", 
@@ -3683,6 +3701,7 @@
       "src/core/surface/server_chttp2.c", 
       "src/core/surface/server_create.c", 
       "src/core/surface/surface_trace.h", 
+      "src/core/surface/validate_metadata.c", 
       "src/core/surface/version.c", 
       "src/core/transport/byte_stream.c", 
       "src/core/transport/byte_stream.h", 
diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json
index d4839e2..2757ce4 100644
--- a/tools/run_tests/tests.json
+++ b/tools/run_tests/tests.json
@@ -1828,6 +1828,23 @@
     "ci_platforms": [
       "linux", 
       "mac", 
+      "posix"
+    ], 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "generic_async_streaming_ping_pong_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
       "posix", 
       "windows"
     ], 
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..a655935
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,26 @@
+# GRPC Python tox (test environment) settings
+[tox]
+skipsdist = true
+envlist = py27
+
+[testenv]
+setenv =
+    PYGRPC_ROOT = {toxinidir}/src/python/grpcio/
+commands =
+    {envpython} setup.py build_py
+    {envpython} setup.py test
+    {envbindir}/coverage combine
+# TODO(atash): we currently ignore cygrpc.pyx due to an insufficiency in Cython's coverage plug-in. Discussion is ongoing.
+    {envbindir}/coverage html --include='{env:PYGRPC_ROOT}/grpc/*' --omit='{env:PYGRPC_ROOT}/grpc/framework/alpha/*','{env:PYGRPC_ROOT}/grpc/early_adopter/*','{env:PYGRPC_ROOT}/grpc/framework/base/*','{env:PYGRPC_ROOT}/grpc/framework/face/*','{env:PYGRPC_ROOT}/grpc/_adapter/fore.py','{env:PYGRPC_ROOT}/grpc/_adapter/rear.py','{env:PYGRPC_ROOT}/grpc/_cython/cygrpc.pyx'
+    {envbindir}/coverage report --include='{env:PYGRPC_ROOT}/grpc/*' --omit='{env:PYGRPC_ROOT}/grpc/framework/alpha/*','{env:PYGRPC_ROOT}/grpc/early_adopter/*','{env:PYGRPC_ROOT}/grpc/framework/base/*','{env:PYGRPC_ROOT}/grpc/framework/face/*','{env:PYGRPC_ROOT}/grpc/_adapter/fore.py','{env:PYGRPC_ROOT}/grpc/_adapter/rear.py','{env:PYGRPC_ROOT}/grpc/_cython/cygrpc.pyx'
+deps =
+    -rrequirements.txt
+passenv = *
+
+[testenv:interop_client]
+commands =
+    {envpython} setup.py run_interop --client --args='{posargs}'
+
+[testenv:interop_server]
+commands =
+    {envpython} setup.py run_interop --server --args='{posargs}'
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj b/vsprojects/vcxproj/grpc/grpc.vcxproj
index f1e780f..8d6800e 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj
@@ -642,6 +642,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\surface\server_create.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\surface\validate_metadata.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\surface\version.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\transport\byte_stream.c">
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
index 055256a..f29f881 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
@@ -352,6 +352,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\surface\server_create.c">
       <Filter>src\core\surface</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\surface\validate_metadata.c">
+      <Filter>src\core\surface</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\surface\version.c">
       <Filter>src\core\surface</Filter>
     </ClCompile>
diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
index b296521..78ffe14 100644
--- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
+++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
@@ -578,6 +578,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\surface\server_create.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\surface\validate_metadata.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\surface\version.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\transport\byte_stream.c">
diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
index e30ca5f..e068c3f 100644
--- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
@@ -292,6 +292,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\surface\server_create.c">
       <Filter>src\core\surface</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\surface\validate_metadata.c">
+      <Filter>src\core\surface</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\surface\version.c">
       <Filter>src\core\surface</Filter>
     </ClCompile>