Merge pull request #9161 from jtattermusch/reenable_timeouts

Reintroduce test timeouts eliminated by mistake
diff --git a/doc/negative-http2-interop-test-descriptions.md b/doc/negative-http2-interop-test-descriptions.md
new file mode 100644
index 0000000..5ea3a96
--- /dev/null
+++ b/doc/negative-http2-interop-test-descriptions.md
@@ -0,0 +1,194 @@
+Negative HTTP/2 Interop Test Case Descriptions
+=======================================
+
+Client and server use
+[test.proto](../src/proto/grpc/testing/test.proto).
+
+Server
+------
+The code for the custom http2 server can be found
+[here](https://github.com/grpc/grpc/tree/master/test/http2_test).
+It is responsible for handling requests and sending responses, and also for 
+fulfilling the behavior of each particular test case.
+
+Server should accept these arguments:
+* --port=PORT
+  * The port the server will run on. For example, "8080"
+* --test_case=TESTCASE
+  * The name of the test case to execute. For example, "goaway"
+
+Client
+------
+
+Clients implement test cases that test certain functionality. Each client is
+provided the test case it is expected to run as a command-line parameter. Names
+should be lowercase and without spaces.
+
+Clients should accept these arguments:
+* --server_host=HOSTNAME
+    * The server host to connect to. For example, "localhost" or "127.0.0.1"
+* --server_port=PORT
+    * The server port to connect to. For example, "8080"
+* --test_case=TESTCASE
+    * The name of the test case to execute. For example, "goaway"
+
+Note
+-----
+
+Note that the server and client must be invoked with the same test case or else
+the test will be meaningless. For convenience, we provide a shell script wrapper
+that invokes both server and client at the same time, with the same test_case.
+This is the preferred way to run these tests.
+
+## Test Cases
+
+### goaway
+
+This test verifies that the client correctly responds to a goaway sent by the
+server. The client should handle the goaway by switching to a new stream without
+the user application having to do a thing.
+
+Client Procedure:
+ 1. Client sends two UnaryCall requests with:
+ 
+    ```
+    {
+      response_size: 314159
+      payload:{
+        body: 271828 bytes of zeros
+      }
+    }
+    ```
+
+Client asserts:
+* Call was successful.
+* Response payload body is 314159 bytes in size.
+
+Server Procedure:
+  1. Server sends a GOAWAY after receiving the first UnaryCall.
+
+Server asserts:
+* The second UnaryCall has a different stream_id than the first one.
+
+### rst_after_header
+
+This test verifies that the client fails correctly when the server sends a
+RST_STREAM immediately after sending headers to the client.
+
+Procedure:
+ 1. Client sends UnaryCall with:
+ 
+    ```
+    {
+      response_size: 314159
+      payload:{
+        body: 271828 bytes of zeros
+      }
+    }
+    ```
+
+Client asserts:
+* Call was not successful.
+
+Server Procedure:
+  1. Server sends a RST_STREAM with error code 0 after sending headers to the client.
+  
+*At the moment the error code and message returned are not standardized throughout all
+languages. Those checks will be added once all client languages behave the same way. [#9142](https://github.com/grpc/grpc/issues/9142) is in flight.*
+
+### rst_during_data
+
+This test verifies that the client fails "correctly" when the server sends a
+RST_STREAM halfway through sending data to the client.
+
+Procedure:
+ 1. Client sends UnaryCall with:
+ 
+    ```
+    {
+      response_size: 314159
+      payload:{
+        body: 271828 bytes of zeros
+      }
+    }
+    ```
+
+Client asserts:
+* Call was not successful.
+
+Server Procedure:
+  1. Server sends a RST_STREAM with error code 0 after sending half of 
+     the requested data to the client.
+
+### rst_after_data
+
+This test verifies that the client fails "correctly" when the server sends a
+RST_STREAM after sending all of the data to the client.
+
+Procedure:
+ 1. Client sends UnaryCall with:
+ 
+    ```
+    {
+      response_size: 314159
+      payload:{
+        body: 271828 bytes of zeros
+      }
+    }
+    ```
+
+Client asserts:
+* Call was not successful.
+
+Server Procedure:
+  1. Server sends a RST_STREAM with error code 0 after sending all of the
+  data to the client.
+
+*Certain client languages allow the data to be accessed even though a RST_STREAM
+was encountered. Once all client languages behave this way, checks will be added on
+the incoming data.*
+
+### ping
+
+This test verifies that the client correctly acknowledges all pings it gets from the
+server.
+
+Procedure:
+ 1. Client sends UnaryCall with:
+ 
+    ```
+    {
+      response_size: 314159
+      payload:{
+        body: 271828 bytes of zeros
+      }
+    }
+    ```
+  
+Client asserts:
+* call was successful.
+* response payload body is 314159 bytes in size.
+
+Server Procedure:
+  1. Server tracks the number of outstanding pings (i.e. +1 when it sends a ping, and -1 
+  when it receives an ack from the client).
+  2. Server sends pings before and after sending headers, also before and after sending data.
+  
+Server Asserts:
+* Number of outstanding pings is 0 when the connection is lost.
+
+### max_streams
+
+This test verifies that the client observes the MAX_CONCURRENT_STREAMS limit set by the server.
+
+Client Procedure:
+  1. Client sends initial UnaryCall to allow the server to update its MAX_CONCURRENT_STREAMS settings.
+  2. Client concurrently sends 10 UnaryCalls.
+  
+Client Asserts:
+* All UnaryCalls were successful, and had the correct type and payload size.
+ 
+Server Procedure:
+  1. Sets MAX_CONCURRENT_STREAMS to one after the connection is made.
+
+*The assertion that the MAX_CONCURRENT_STREAMS limit is upheld occurs in the http2 library we used.*
diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec
index 04f7211..e10e534 100644
--- a/gRPC-Core.podspec
+++ b/gRPC-Core.podspec
@@ -35,7 +35,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC-Core'
-  version = '1.0.1'
+  version = '1.0.2'
   s.version  = version
   s.summary  = 'Core cross-platform gRPC library, written in C'
   s.homepage = 'http://www.grpc.io'
@@ -44,7 +44,9 @@
 
   s.source = {
     :git => 'https://github.com/grpc/grpc.git',
-    :tag => "v#{version}",
+    # TODO(mxyan): Change back to "v#{version}" for next release
+    #:tag => "v#{version}",
+    :tag => "objective-c-v#{version}",
     # TODO(jcanizales): Depend explicitly on the nanopb pod, and disable submodules.
     :submodules => true,
   }
diff --git a/gRPC-ProtoRPC.podspec b/gRPC-ProtoRPC.podspec
index 61d4b62..62eaa2a 100644
--- a/gRPC-ProtoRPC.podspec
+++ b/gRPC-ProtoRPC.podspec
@@ -30,7 +30,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC-ProtoRPC'
-  version = '1.0.1'
+  version = '1.0.2'
   s.version  = version
   s.summary  = 'RPC library for Protocol Buffers, based on gRPC'
   s.homepage = 'http://www.grpc.io'
@@ -39,7 +39,7 @@
 
   s.source = {
     :git => 'https://github.com/grpc/grpc.git',
-    :tag => "v#{version}",
+    :tag => "objective-c-v#{version}",
   }
 
   s.ios.deployment_target = '7.1'
diff --git a/gRPC-RxLibrary.podspec b/gRPC-RxLibrary.podspec
index d59385c..2e8fffd 100644
--- a/gRPC-RxLibrary.podspec
+++ b/gRPC-RxLibrary.podspec
@@ -30,7 +30,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC-RxLibrary'
-  version = '1.0.1'
+  version = '1.0.2'
   s.version  = version
   s.summary  = 'Reactive Extensions library for iOS/OSX.'
   s.homepage = 'http://www.grpc.io'
@@ -39,7 +39,7 @@
 
   s.source = {
     :git => 'https://github.com/grpc/grpc.git',
-    :tag => "v#{version}",
+    :tag => "objective-c-v#{version}",
   }
 
   s.ios.deployment_target = '7.1'
diff --git a/gRPC.podspec b/gRPC.podspec
index 76410b1..e8b7709 100644
--- a/gRPC.podspec
+++ b/gRPC.podspec
@@ -30,7 +30,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC'
-  version = '1.0.1'
+  version = '1.0.2'
   s.version  = version
   s.summary  = 'gRPC client library for iOS/OSX'
   s.homepage = 'http://www.grpc.io'
@@ -39,7 +39,7 @@
 
   s.source = {
     :git => 'https://github.com/grpc/grpc.git',
-    :tag => "v#{version}",
+    :tag => "objective-c-v#{version}",
   }
 
   s.ios.deployment_target = '7.1'
diff --git a/grpc.gemspec b/grpc.gemspec
index 6019b97..9cafd1f 100755
--- a/grpc.gemspec
+++ b/grpc.gemspec
@@ -27,7 +27,7 @@
   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.2'
+  s.add_dependency 'google-protobuf', '~> 3.1.0'
   s.add_dependency 'googleauth',      '~> 0.5.1'
 
   s.add_development_dependency 'bundler',            '~> 1.9'
diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h
index 5e48621..898f4d5 100644
--- a/include/grpc/grpc.h
+++ b/include/grpc/grpc.h
@@ -202,9 +202,15 @@
     completion of type 'tag' to the completion queue bound to the call.
     The order of ops specified in the batch has no significance.
     Only one operation of each type can be active at once in any given
-    batch. You must call grpc_completion_queue_next or
-    grpc_completion_queue_pluck on the completion queue associated with 'call'
-    for work to be performed.
+    batch.
+    If a call to grpc_call_start_batch returns GRPC_CALL_OK you must call
+    grpc_completion_queue_next or grpc_completion_queue_pluck on the completion
+    queue associated with 'call' for work to be performed. If a call to
+    grpc_call_start_batch returns any value other than GRPC_CALL_OK it is
+    guaranteed that no state associated with 'call' is changed and it is not
+    appropriate to call grpc_completion_queue_next or
+    grpc_completion_queue_pluck consequent to the failed grpc_call_start_batch
+    call.
     THREAD SAFETY: access to grpc_call_start_batch in multi-threaded environment
     needs to be synchronized. As an optimization, you may synchronize batches
     containing just send operations independently from batches containing just
diff --git a/src/compiler/python_generator.cc b/src/compiler/python_generator.cc
index b0a6009..0fac1b8 100644
--- a/src/compiler/python_generator.cc
+++ b/src/compiler/python_generator.cc
@@ -40,6 +40,7 @@
 #include <map>
 #include <memory>
 #include <ostream>
+#include <set>
 #include <sstream>
 #include <tuple>
 #include <vector>
@@ -64,7 +65,9 @@
 using std::map;
 using std::pair;
 using std::replace;
+using std::tuple;
 using std::vector;
+using std::set;
 
 namespace grpc_python_generator {
 
@@ -73,6 +76,8 @@
 typedef vector<const Descriptor*> DescriptorVector;
 typedef map<grpc::string, grpc::string> StringMap;
 typedef vector<grpc::string> StringVector;
+typedef tuple<grpc::string, grpc::string> StringPair;
+typedef set<StringPair> StringPairSet;
 
 // Provides RAII indentation handling. Use as:
 // {
@@ -651,6 +656,7 @@
       "face_utilities\n");
   if (generate_in_pb2_grpc) {
     out->Print("\n");
+    StringPairSet imports_set;
     for (int i = 0; i < file->service_count(); ++i) {
       const ServiceDescriptor* service = file->service(i);
       for (int j = 0; j < service->method_count(); ++j) {
@@ -662,11 +668,15 @@
           grpc::string type_file_name = type->file()->name();
           grpc::string module_name = ModuleName(type_file_name);
           grpc::string module_alias = ModuleAlias(type_file_name);
-          out->Print("import $ModuleName$ as $ModuleAlias$\n", "ModuleName",
-                     module_name, "ModuleAlias", module_alias);
+          imports_set.insert(std::make_tuple(module_name, module_alias));
         }
       }
     }
+    for (StringPairSet::iterator it = imports_set.begin();
+         it != imports_set.end(); ++it) {
+      out->Print("import $ModuleName$ as $ModuleAlias$\n", "ModuleName",
+                 std::get<0>(*it), "ModuleAlias", std::get<1>(*it));
+    }
   }
   return true;
 }
diff --git a/src/core/ext/client_channel/client_channel.c b/src/core/ext/client_channel/client_channel.c
index 5698d65..9d46338 100644
--- a/src/core/ext/client_channel/client_channel.c
+++ b/src/core/ext/client_channel/client_channel.c
@@ -526,7 +526,9 @@
   arg = grpc_channel_args_find(args->channel_args, GRPC_ARG_SERVER_URI);
   GPR_ASSERT(arg != NULL);
   GPR_ASSERT(arg->type == GRPC_ARG_STRING);
-  chand->resolver = grpc_resolver_create(arg->value.string, args->channel_args);
+  chand->resolver =
+      grpc_resolver_create(exec_ctx, arg->value.string, args->channel_args,
+                           chand->interested_parties);
   if (chand->resolver == NULL) {
     return GRPC_ERROR_CREATE("resolver creation failed");
   }
diff --git a/src/core/ext/client_channel/resolver_factory.c b/src/core/ext/client_channel/resolver_factory.c
index 7c3d644..00bbb92 100644
--- a/src/core/ext/client_channel/resolver_factory.c
+++ b/src/core/ext/client_channel/resolver_factory.c
@@ -43,9 +43,10 @@
 
 /** Create a resolver instance for a name */
 grpc_resolver* grpc_resolver_factory_create_resolver(
-    grpc_resolver_factory* factory, grpc_resolver_args* args) {
+    grpc_exec_ctx* exec_ctx, grpc_resolver_factory* factory,
+    grpc_resolver_args* args) {
   if (factory == NULL) return NULL;
-  return factory->vtable->create_resolver(factory, args);
+  return factory->vtable->create_resolver(exec_ctx, factory, args);
 }
 
 char* grpc_resolver_factory_get_default_authority(
diff --git a/src/core/ext/client_channel/resolver_factory.h b/src/core/ext/client_channel/resolver_factory.h
index 4da42e8..3792ddc 100644
--- a/src/core/ext/client_channel/resolver_factory.h
+++ b/src/core/ext/client_channel/resolver_factory.h
@@ -37,6 +37,7 @@
 #include "src/core/ext/client_channel/client_channel_factory.h"
 #include "src/core/ext/client_channel/resolver.h"
 #include "src/core/ext/client_channel/uri_parser.h"
+#include "src/core/lib/iomgr/pollset_set.h"
 
 typedef struct grpc_resolver_factory grpc_resolver_factory;
 typedef struct grpc_resolver_factory_vtable grpc_resolver_factory_vtable;
@@ -48,6 +49,7 @@
 typedef struct grpc_resolver_args {
   grpc_uri *uri;
   const grpc_channel_args *args;
+  grpc_pollset_set *pollset_set;
 } grpc_resolver_args;
 
 struct grpc_resolver_factory_vtable {
@@ -55,7 +57,8 @@
   void (*unref)(grpc_resolver_factory *factory);
 
   /** Implementation of grpc_resolver_factory_create_resolver */
-  grpc_resolver *(*create_resolver)(grpc_resolver_factory *factory,
+  grpc_resolver *(*create_resolver)(grpc_exec_ctx *exec_ctx,
+                                    grpc_resolver_factory *factory,
                                     grpc_resolver_args *args);
 
   /** Implementation of grpc_resolver_factory_get_default_authority */
@@ -70,7 +73,8 @@
 
 /** Create a resolver instance for a name */
 grpc_resolver *grpc_resolver_factory_create_resolver(
-    grpc_resolver_factory *factory, grpc_resolver_args *args);
+    grpc_exec_ctx *exec_ctx, grpc_resolver_factory *factory,
+    grpc_resolver_args *args);
 
 /** Return a (freshly allocated with gpr_malloc) string representing
     the default authority to use for this scheme. */
diff --git a/src/core/ext/client_channel/resolver_registry.c b/src/core/ext/client_channel/resolver_registry.c
index 2b62b97..5110a7c 100644
--- a/src/core/ext/client_channel/resolver_registry.c
+++ b/src/core/ext/client_channel/resolver_registry.c
@@ -131,8 +131,9 @@
   return factory;
 }
 
-grpc_resolver *grpc_resolver_create(const char *target,
-                                    const grpc_channel_args *args) {
+grpc_resolver *grpc_resolver_create(grpc_exec_ctx *exec_ctx, const char *target,
+                                    const grpc_channel_args *args,
+                                    grpc_pollset_set *pollset_set) {
   grpc_uri *uri = NULL;
   char *canonical_target = NULL;
   grpc_resolver_factory *factory =
@@ -142,7 +143,9 @@
   memset(&resolver_args, 0, sizeof(resolver_args));
   resolver_args.uri = uri;
   resolver_args.args = args;
-  resolver = grpc_resolver_factory_create_resolver(factory, &resolver_args);
+  resolver_args.pollset_set = pollset_set;
+  resolver =
+      grpc_resolver_factory_create_resolver(exec_ctx, factory, &resolver_args);
   grpc_uri_destroy(uri);
   gpr_free(canonical_target);
   return resolver;
diff --git a/src/core/ext/client_channel/resolver_registry.h b/src/core/ext/client_channel/resolver_registry.h
index 24678bc..4fb1613 100644
--- a/src/core/ext/client_channel/resolver_registry.h
+++ b/src/core/ext/client_channel/resolver_registry.h
@@ -35,6 +35,7 @@
 #define GRPC_CORE_EXT_CLIENT_CHANNEL_RESOLVER_REGISTRY_H
 
 #include "src/core/ext/client_channel/resolver_factory.h"
+#include "src/core/lib/iomgr/pollset_set.h"
 
 void grpc_resolver_registry_init();
 void grpc_resolver_registry_shutdown(void);
@@ -60,8 +61,9 @@
     If a resolver factory was not found, return NULL.
     \a args is a set of channel arguments to be included in the result
     (typically the set of arguments passed in from the client API). */
-grpc_resolver *grpc_resolver_create(const char *target,
-                                    const grpc_channel_args *args);
+grpc_resolver *grpc_resolver_create(grpc_exec_ctx *exec_ctx, const char *target,
+                                    const grpc_channel_args *args,
+                                    grpc_pollset_set *pollset_set);
 
 /** Find a resolver factory given a name and return an (owned-by-the-caller)
  *  reference to it */
diff --git a/src/core/ext/lb_policy/grpclb/grpclb.c b/src/core/ext/lb_policy/grpclb/grpclb.c
index 38eebdc..bed5e6c 100644
--- a/src/core/ext/lb_policy/grpclb/grpclb.c
+++ b/src/core/ext/lb_policy/grpclb/grpclb.c
@@ -768,8 +768,14 @@
   arg = grpc_channel_args_find(args->args, GRPC_ARG_SERVER_URI);
   GPR_ASSERT(arg != NULL);
   GPR_ASSERT(arg->type == GRPC_ARG_STRING);
-  grpc_uri *uri = grpc_uri_parse(arg->value.string, 1);
-  glb_policy->server_name = gpr_strdup(uri->path);
+  grpc_uri *uri = grpc_uri_parse(arg->value.string, true);
+  GPR_ASSERT(uri->path[0] != '\0');
+  glb_policy->server_name =
+      gpr_strdup(uri->path[0] == '/' ? uri->path + 1 : uri->path);
+  if (grpc_lb_glb_trace) {
+    gpr_log(GPR_INFO, "Will use '%s' as the server name for LB request.",
+            glb_policy->server_name);
+  }
   grpc_uri_destroy(uri);
 
   /* All input addresses in addresses come from a resolver that claims
diff --git a/src/core/ext/resolver/dns/native/dns_resolver.c b/src/core/ext/resolver/dns/native/dns_resolver.c
index a7392e1..2675fa9 100644
--- a/src/core/ext/resolver/dns/native/dns_resolver.c
+++ b/src/core/ext/resolver/dns/native/dns_resolver.c
@@ -61,6 +61,8 @@
   char *default_port;
   /** channel args. */
   grpc_channel_args *channel_args;
+  /** pollset_set to drive the name resolution process */
+  grpc_pollset_set *interested_parties;
 
   /** mutex guarding the rest of the state */
   gpr_mu mu;
@@ -218,6 +220,7 @@
   r->resolving = true;
   r->addresses = NULL;
   grpc_resolve_address(exec_ctx, r->name_to_resolve, r->default_port,
+                       r->interested_parties,
                        grpc_closure_create(dns_on_resolved, r), &r->addresses);
 }
 
@@ -240,13 +243,15 @@
   if (r->resolved_result != NULL) {
     grpc_channel_args_destroy(r->resolved_result);
   }
+  grpc_pollset_set_destroy(r->interested_parties);
   gpr_free(r->name_to_resolve);
   gpr_free(r->default_port);
   grpc_channel_args_destroy(r->channel_args);
   gpr_free(r);
 }
 
-static grpc_resolver *dns_create(grpc_resolver_args *args,
+static grpc_resolver *dns_create(grpc_exec_ctx *exec_ctx,
+                                 grpc_resolver_args *args,
                                  const char *default_port) {
   if (0 != strcmp(args->uri->authority, "")) {
     gpr_log(GPR_ERROR, "authority based dns uri's not supported");
@@ -265,6 +270,11 @@
   r->name_to_resolve = proxy_name == NULL ? gpr_strdup(path) : proxy_name;
   r->default_port = gpr_strdup(default_port);
   r->channel_args = grpc_channel_args_copy(args->args);
+  r->interested_parties = grpc_pollset_set_create();
+  if (args->pollset_set != NULL) {
+    grpc_pollset_set_add_pollset_set(exec_ctx, r->interested_parties,
+                                     args->pollset_set);
+  }
   gpr_backoff_init(&r->backoff_state, GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS,
                    GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER,
                    GRPC_DNS_RECONNECT_JITTER,
@@ -282,8 +292,9 @@
 static void dns_factory_unref(grpc_resolver_factory *factory) {}
 
 static grpc_resolver *dns_factory_create_resolver(
-    grpc_resolver_factory *factory, grpc_resolver_args *args) {
-  return dns_create(args, "https");
+    grpc_exec_ctx *exec_ctx, grpc_resolver_factory *factory,
+    grpc_resolver_args *args) {
+  return dns_create(exec_ctx, args, "https");
 }
 
 static char *dns_factory_get_default_host_name(grpc_resolver_factory *factory,
diff --git a/src/core/ext/resolver/sockaddr/sockaddr_resolver.c b/src/core/ext/resolver/sockaddr/sockaddr_resolver.c
index 0a9b1aa..88808c6 100644
--- a/src/core/ext/resolver/sockaddr/sockaddr_resolver.c
+++ b/src/core/ext/resolver/sockaddr/sockaddr_resolver.c
@@ -214,7 +214,8 @@
 
 #define DECL_FACTORY(name)                                                  \
   static grpc_resolver *name##_factory_create_resolver(                     \
-      grpc_resolver_factory *factory, grpc_resolver_args *args) {           \
+      grpc_exec_ctx *exec_ctx, grpc_resolver_factory *factory,              \
+      grpc_resolver_args *args) {                                           \
     return sockaddr_create(args, parse_##name);                             \
   }                                                                         \
   static const grpc_resolver_factory_vtable name##_factory_vtable = {       \
diff --git a/src/core/lib/http/httpcli.c b/src/core/lib/http/httpcli.c
index fdb8aba..1035f31 100644
--- a/src/core/lib/http/httpcli.c
+++ b/src/core/lib/http/httpcli.c
@@ -278,6 +278,7 @@
   grpc_polling_entity_add_to_pollset_set(exec_ctx, req->pollent,
                                          req->context->pollset_set);
   grpc_resolve_address(exec_ctx, request->host, req->handshaker->default_port,
+                       req->context->pollset_set,
                        grpc_closure_create(on_resolved, req), &req->addresses);
 }
 
diff --git a/src/core/lib/iomgr/resolve_address.h b/src/core/lib/iomgr/resolve_address.h
index 2759244..e03d16f 100644
--- a/src/core/lib/iomgr/resolve_address.h
+++ b/src/core/lib/iomgr/resolve_address.h
@@ -36,6 +36,7 @@
 
 #include <stddef.h>
 #include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/pollset_set.h"
 
 #define GRPC_MAX_SOCKADDR_SIZE 128
 
@@ -54,6 +55,7 @@
 /* TODO(ctiller): add a timeout here */
 extern void (*grpc_resolve_address)(grpc_exec_ctx *exec_ctx, const char *addr,
                                     const char *default_port,
+                                    grpc_pollset_set *interested_parties,
                                     grpc_closure *on_done,
                                     grpc_resolved_addresses **addresses);
 /* Destroy resolved addresses */
diff --git a/src/core/lib/iomgr/resolve_address_posix.c b/src/core/lib/iomgr/resolve_address_posix.c
index de791b2..821932e 100644
--- a/src/core/lib/iomgr/resolve_address_posix.c
+++ b/src/core/lib/iomgr/resolve_address_posix.c
@@ -181,6 +181,7 @@
 
 static void resolve_address_impl(grpc_exec_ctx *exec_ctx, const char *name,
                                  const char *default_port,
+                                 grpc_pollset_set *interested_parties,
                                  grpc_closure *on_done,
                                  grpc_resolved_addresses **addrs) {
   request *r = gpr_malloc(sizeof(request));
@@ -192,9 +193,9 @@
   grpc_executor_push(&r->request_closure, GRPC_ERROR_NONE);
 }
 
-void (*grpc_resolve_address)(grpc_exec_ctx *exec_ctx, const char *name,
-                             const char *default_port, grpc_closure *on_done,
-                             grpc_resolved_addresses **addrs) =
-    resolve_address_impl;
+void (*grpc_resolve_address)(
+    grpc_exec_ctx *exec_ctx, const char *name, const char *default_port,
+    grpc_pollset_set *interested_parties, grpc_closure *on_done,
+    grpc_resolved_addresses **addrs) = resolve_address_impl;
 
 #endif
diff --git a/src/core/lib/iomgr/resolve_address_uv.c b/src/core/lib/iomgr/resolve_address_uv.c
index b8295ac..3269c4f 100644
--- a/src/core/lib/iomgr/resolve_address_uv.c
+++ b/src/core/lib/iomgr/resolve_address_uv.c
@@ -181,6 +181,7 @@
 
 static void resolve_address_impl(grpc_exec_ctx *exec_ctx, const char *name,
                                  const char *default_port,
+                                 grpc_pollset_set *interested_parties,
                                  grpc_closure *on_done,
                                  grpc_resolved_addresses **addrs) {
   uv_getaddrinfo_t *req;
@@ -223,9 +224,9 @@
   }
 }
 
-void (*grpc_resolve_address)(grpc_exec_ctx *exec_ctx, const char *name,
-                             const char *default_port, grpc_closure *on_done,
-                             grpc_resolved_addresses **addrs) =
-    resolve_address_impl;
+void (*grpc_resolve_address)(
+    grpc_exec_ctx *exec_ctx, const char *name, const char *default_port,
+    grpc_pollset_set *interested_parties, grpc_closure *on_done,
+    grpc_resolved_addresses **addrs) = resolve_address_impl;
 
 #endif /* GRPC_UV */
diff --git a/src/core/lib/iomgr/resolve_address_windows.c b/src/core/lib/iomgr/resolve_address_windows.c
index e139293..fada5ec 100644
--- a/src/core/lib/iomgr/resolve_address_windows.c
+++ b/src/core/lib/iomgr/resolve_address_windows.c
@@ -169,6 +169,7 @@
 
 static void resolve_address_impl(grpc_exec_ctx *exec_ctx, const char *name,
                                  const char *default_port,
+                                 grpc_pollset_set *interested_parties,
                                  grpc_closure *on_done,
                                  grpc_resolved_addresses **addresses) {
   request *r = gpr_malloc(sizeof(request));
@@ -180,9 +181,9 @@
   grpc_executor_push(&r->request_closure, GRPC_ERROR_NONE);
 }
 
-void (*grpc_resolve_address)(grpc_exec_ctx *exec_ctx, const char *name,
-                             const char *default_port, grpc_closure *on_done,
-                             grpc_resolved_addresses **addresses) =
-    resolve_address_impl;
+void (*grpc_resolve_address)(
+    grpc_exec_ctx *exec_ctx, const char *name, const char *default_port,
+    grpc_pollset_set *interested_parties, grpc_closure *on_done,
+    grpc_resolved_addresses **addresses) = resolve_address_impl;
 
 #endif
diff --git a/src/core/lib/security/transport/secure_endpoint.c b/src/core/lib/security/transport/secure_endpoint.c
index 1b27841..331a8f1 100644
--- a/src/core/lib/security/transport/secure_endpoint.c
+++ b/src/core/lib/security/transport/secure_endpoint.c
@@ -372,7 +372,10 @@
   return grpc_endpoint_get_peer(ep->wrapped_ep);
 }
 
-static int endpoint_get_fd(grpc_endpoint *secure_ep) { return -1; }
+static int endpoint_get_fd(grpc_endpoint *secure_ep) {
+  secure_endpoint *ep = (secure_endpoint *)secure_ep;
+  return grpc_endpoint_get_fd(ep->wrapped_ep);
+}
 
 static grpc_workqueue *endpoint_get_workqueue(grpc_endpoint *secure_ep) {
   secure_endpoint *ep = (secure_endpoint *)secure_ep;
diff --git a/src/node/src/client.js b/src/node/src/client.js
index 0f85f2c..134ef23 100644
--- a/src/node/src/client.js
+++ b/src/node/src/client.js
@@ -99,7 +99,18 @@
 function _write(chunk, encoding, callback) {
   /* jshint validthis: true */
   var batch = {};
-  var message = this.serialize(chunk);
+  var message;
+  try {
+    message = this.serialize(chunk);
+  } catch (e) {
+    /* Sending this error to the server and emitting it immediately on the
+       client may put the call in a slightly weird state on the client side,
+       but passing an object that causes a serialization failure is a misuse
+       of the API anyway, so that's OK. The primary purpose here is to give the
+       programmer a useful error and to stop the stream properly */
+    this.call.cancelWithStatus(grpc.status.INTERNAL, "Serialization failure");
+    callback(e);
+  }
   if (_.isFinite(encoding)) {
     /* Attach the encoding if it is a finite number. This is the closest we
      * can get to checking that it is valid flags */
diff --git a/src/node/src/server.js b/src/node/src/server.js
index b3b4149..da9c6b2 100644
--- a/src/node/src/server.js
+++ b/src/node/src/server.js
@@ -127,7 +127,14 @@
         (new Metadata())._getCoreRepresentation();
     call.metadataSent = true;
   }
-  var message = serialize(value);
+  var message;
+  try {
+    message = serialize(value);
+  } catch (e) {
+    e.code = grpc.status.INTERNAL;
+    handleError(e);
+    return;
+  }
   message.grpcWriteFlags = flags;
   end_batch[grpc.opType.SEND_MESSAGE] = message;
   end_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = status;
@@ -278,7 +285,14 @@
         (new Metadata())._getCoreRepresentation();
     this.call.metadataSent = true;
   }
-  var message = this.serialize(chunk);
+  var message;
+  try {
+    message = this.serialize(chunk);
+  } catch (e) {
+    e.code = grpc.status.INTERNAL;
+    callback(e);
+    return;
+  }
   if (_.isFinite(encoding)) {
     /* Attach the encoding if it is a finite number. This is the closest we
      * can get to checking that it is valid flags */
diff --git "a/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec" "b/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec"
index 6e594fd..bcc2bb6 100644
--- "a/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec"
+++ "b/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec"
@@ -36,7 +36,7 @@
   # exclamation mark ensures that other "regular" pods will be able to find it as it'll be installed
   # before them.
   s.name     = '!ProtoCompiler-gRPCPlugin'
-  v = '1.0.1'
+  v = '1.0.2'
   s.version  = v
   s.summary  = 'The gRPC ProtoC plugin generates Objective-C files from .proto services.'
   s.description = <<-DESC
@@ -84,7 +84,10 @@
   repo = 'grpc/grpc'
   file = "grpc_objective_c_plugin-#{v}-macos-x86_64.zip"
   s.source = {
-    :http => "https://github.com/#{repo}/releases/download/v#{v}/#{file}",
+    # TODO(mxyan): Change back to "https://github.com/#{repo}/releases/download/v#{v}/#{file}" for
+    # next release
+    # :http => "https://github.com/#{repo}/releases/download/v#{v}/#{file}",
+    :http => "https://github.com/#{repo}/releases/download/objective-c-v#{v}/#{file}",
     # TODO(jcanizales): Add sha1 or sha256
     # :sha1 => '??',
   }
diff --git a/src/objective-c/GRPCClient/private/GRPCHost.m b/src/objective-c/GRPCClient/private/GRPCHost.m
index 31065cb..450bec3 100644
--- a/src/objective-c/GRPCClient/private/GRPCHost.m
+++ b/src/objective-c/GRPCClient/private/GRPCHost.m
@@ -50,7 +50,7 @@
 
 // TODO(jcanizales): Generate the version in a standalone header, from templates. Like
 // templates/src/core/surface/version.c.template .
-#define GRPC_OBJC_VERSION_STRING @"1.0.1"
+#define GRPC_OBJC_VERSION_STRING @"1.0.2"
 
 static NSMutableDictionary *kHostCache;
 
diff --git a/src/python/grpcio/grpc/__init__.py b/src/python/grpcio/grpc/__init__.py
index 9defb31..e3c1015 100644
--- a/src/python/grpcio/grpc/__init__.py
+++ b/src/python/grpcio/grpc/__init__.py
@@ -768,8 +768,8 @@
     gRPC runtime to determine the status code of the RPC.
 
     Args:
-      code: The integer status code of the RPC to be transmitted to the
-        invocation side of the RPC.
+      code: A StatusCode value to be transmitted to the invocation side of the
+        RPC as the status code of the RPC.
     """
     raise NotImplementedError()
 
@@ -781,8 +781,8 @@
     details to transmit.
 
     Args:
-      details: The details string of the RPC to be transmitted to
-        the invocation side of the RPC.
+      details: A string to be transmitted to the invocation side of the RPC as
+        the status details of the RPC.
     """
     raise NotImplementedError()
 
diff --git a/src/python/grpcio/grpc/_channel.py b/src/python/grpcio/grpc/_channel.py
index 53a2672..41e9163 100644
--- a/src/python/grpcio/grpc/_channel.py
+++ b/src/python/grpcio/grpc/_channel.py
@@ -36,8 +36,8 @@
 import grpc
 from grpc import _common
 from grpc import _grpcio_metadata
-from grpc.framework.foundation import callable_util
 from grpc._cython import cygrpc
+from grpc.framework.foundation import callable_util
 
 _USER_AGENT = 'Python-gRPC-{}'.format(_grpcio_metadata.__version__)
 
@@ -99,6 +99,22 @@
     else:
       condition.wait(timeout=remaining)
 
+_INTERNAL_CALL_ERROR_MESSAGE_FORMAT = (
+    'Internal gRPC call error %d. ' +
+    'Please report to https://github.com/grpc/grpc/issues')
+
+def _check_call_error(call_error, metadata):
+  if call_error == cygrpc.CallError.invalid_metadata:
+    raise ValueError('metadata was invalid: %s' % metadata)
+  elif call_error != cygrpc.CallError.ok:
+    raise ValueError(_INTERNAL_CALL_ERROR_MESSAGE_FORMAT % call_error)
+
+def _call_error_set_RPCstate(state, call_error, metadata):
+  if call_error == cygrpc.CallError.invalid_metadata:
+    _abort(state, grpc.StatusCode.INTERNAL, 'metadata was invalid: %s' % metadata)
+  else:
+    _abort(state, grpc.StatusCode.INTERNAL, 
+        _INTERNAL_CALL_ERROR_MESSAGE_FORMAT % call_error)
 
 class _RPCState(object):
 
@@ -358,7 +374,7 @@
       if self._state.callbacks is None:
         return False
       else:
-        self._state.callbacks.append(lambda: callback())
+        self._state.callbacks.append(callback)
         return True
 
   def initial_metadata(self):
@@ -435,10 +451,10 @@
 class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
 
   def __init__(
-      self, channel, create_managed_call, method, request_serializer,
+      self, channel, managed_call, method, request_serializer,
       response_deserializer):
     self._channel = channel
-    self._create_managed_call = create_managed_call
+    self._managed_call = managed_call
     self._method = method
     self._request_serializer = request_serializer
     self._response_deserializer = response_deserializer
@@ -472,7 +488,8 @@
           None, 0, completion_queue, self._method, None, deadline_timespec)
       if credentials is not None:
         call.set_credentials(credentials._credentials)
-      call.start_client_batch(cygrpc.Operations(operations), None)
+      call_error = call.start_client_batch(cygrpc.Operations(operations), None)
+      _check_call_error(call_error, metadata)
       _handle_event(completion_queue.poll(), state, self._response_deserializer)
       return state, deadline
 
@@ -490,23 +507,28 @@
     if rendezvous:
       return rendezvous
     else:
-      call = self._create_managed_call(
+      call, drive_call = self._managed_call(
           None, 0, self._method, None, deadline_timespec)
       if credentials is not None:
         call.set_credentials(credentials._credentials)
       event_handler = _event_handler(state, call, self._response_deserializer)
       with state.condition:
-        call.start_client_batch(cygrpc.Operations(operations), event_handler)
+        call_error = call.start_client_batch(cygrpc.Operations(operations),
+            event_handler)
+        if call_error != cygrpc.CallError.ok:
+          _call_error_set_RPCstate(state, call_error, metadata)
+          return _Rendezvous(state, None, None, deadline)
+        drive_call()
       return _Rendezvous(state, call, self._response_deserializer, deadline)
 
 
 class _UnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable):
 
   def __init__(
-      self, channel, create_managed_call, method, request_serializer,
+      self, channel, managed_call, method, request_serializer,
       response_deserializer):
     self._channel = channel
-    self._create_managed_call = create_managed_call
+    self._managed_call = managed_call
     self._method = method
     self._request_serializer = request_serializer
     self._response_deserializer = response_deserializer
@@ -518,7 +540,7 @@
       raise rendezvous
     else:
       state = _RPCState(_UNARY_STREAM_INITIAL_DUE, None, None, None, None)
-      call = self._create_managed_call(
+      call, drive_call = self._managed_call(
           None, 0, self._method, None, deadline_timespec)
       if credentials is not None:
         call.set_credentials(credentials._credentials)
@@ -535,17 +557,22 @@
             cygrpc.operation_send_close_from_client(_EMPTY_FLAGS),
             cygrpc.operation_receive_status_on_client(_EMPTY_FLAGS),
         )
-        call.start_client_batch(cygrpc.Operations(operations), event_handler)
+        call_error = call.start_client_batch(cygrpc.Operations(operations),
+            event_handler)
+        if call_error != cygrpc.CallError.ok:
+          _call_error_set_RPCstate(state, call_error, metadata)
+          return _Rendezvous(state, None, None, deadline)
+        drive_call()
       return _Rendezvous(state, call, self._response_deserializer, deadline)
 
 
 class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
 
   def __init__(
-      self, channel, create_managed_call, method, request_serializer,
+      self, channel, managed_call, method, request_serializer,
       response_deserializer):
     self._channel = channel
-    self._create_managed_call = create_managed_call
+    self._managed_call = managed_call
     self._method = method
     self._request_serializer = request_serializer
     self._response_deserializer = response_deserializer
@@ -569,7 +596,8 @@
           cygrpc.operation_receive_message(_EMPTY_FLAGS),
           cygrpc.operation_receive_status_on_client(_EMPTY_FLAGS),
       )
-      call.start_client_batch(cygrpc.Operations(operations), None)
+      call_error = call.start_client_batch(cygrpc.Operations(operations), None)
+      _check_call_error(call_error, metadata)
       _consume_request_iterator(
           request_iterator, state, call, self._request_serializer)
     while True:
@@ -597,7 +625,7 @@
       self, request_iterator, timeout=None, metadata=None, credentials=None):
     deadline, deadline_timespec = _deadline(timeout)
     state = _RPCState(_STREAM_UNARY_INITIAL_DUE, None, None, None, None)
-    call = self._create_managed_call(
+    call, drive_call = self._managed_call(
         None, 0, self._method, None, deadline_timespec)
     if credentials is not None:
       call.set_credentials(credentials._credentials)
@@ -613,7 +641,12 @@
           cygrpc.operation_receive_message(_EMPTY_FLAGS),
           cygrpc.operation_receive_status_on_client(_EMPTY_FLAGS),
       )
-      call.start_client_batch(cygrpc.Operations(operations), event_handler)
+      call_error = call.start_client_batch(cygrpc.Operations(operations),
+          event_handler)
+      if call_error != cygrpc.CallError.ok:
+        _call_error_set_RPCstate(state, call_error, metadata)
+        return _Rendezvous(state, None, None, deadline)
+      drive_call()
       _consume_request_iterator(
           request_iterator, state, call, self._request_serializer)
     return _Rendezvous(state, call, self._response_deserializer, deadline)
@@ -622,10 +655,10 @@
 class _StreamStreamMultiCallable(grpc.StreamStreamMultiCallable):
 
   def __init__(
-      self, channel, create_managed_call, method, request_serializer,
+      self, channel, managed_call, method, request_serializer,
       response_deserializer):
     self._channel = channel
-    self._create_managed_call = create_managed_call
+    self._managed_call = managed_call
     self._method = method
     self._request_serializer = request_serializer
     self._response_deserializer = response_deserializer
@@ -634,7 +667,7 @@
       self, request_iterator, timeout=None, metadata=None, credentials=None):
     deadline, deadline_timespec = _deadline(timeout)
     state = _RPCState(_STREAM_STREAM_INITIAL_DUE, None, None, None, None)
-    call = self._create_managed_call(
+    call, drive_call = self._managed_call(
         None, 0, self._method, None, deadline_timespec)
     if credentials is not None:
       call.set_credentials(credentials._credentials)
@@ -649,7 +682,12 @@
               _common.cygrpc_metadata(metadata), _EMPTY_FLAGS),
           cygrpc.operation_receive_status_on_client(_EMPTY_FLAGS),
       )
-      call.start_client_batch(cygrpc.Operations(operations), event_handler)
+      call_error = call.start_client_batch(cygrpc.Operations(operations),
+          event_handler)
+      if call_error != cygrpc.CallError.ok:
+        _call_error_set_RPCstate(state, call_error, metadata)
+        return _Rendezvous(state, None, None, deadline)
+      drive_call()
       _consume_request_iterator(
           request_iterator, state, call, self._request_serializer)
     return _Rendezvous(state, call, self._response_deserializer, deadline)
@@ -687,16 +725,13 @@
   channel_spin_thread.start()
 
 
-def _create_channel_managed_call(state):
-  def create_channel_managed_call(parent, flags, method, host, deadline):
-    """Creates a managed cygrpc.Call.
+def _channel_managed_call_management(state):
+  def create(parent, flags, method, host, deadline):
+    """Creates a managed cygrpc.Call and a function to call to drive it.
 
-    Callers of this function must conduct at least one operation on the returned
-    call. The tags associated with operations conducted on the returned call
-    must be no-argument callables that return None to indicate that this channel
-    should continue polling for events associated with the call and return the
-    call itself to indicate that no more events associated with the call will be
-    generated.
+    If operations are successfully added to the returned cygrpc.Call, the
+    returned function must be called. If operations are not successfully added
+    to the returned cygrpc.Call, the returned function must not be called.
 
     Args:
       parent: A cygrpc.Call to be used as the parent of the created call.
@@ -706,18 +741,22 @@
       deadline: A cygrpc.Timespec to be the deadline of the created call.
 
     Returns:
-      A cygrpc.Call with which to conduct an RPC.
+      A cygrpc.Call with which to conduct an RPC and a function to call if
+        operations are successfully started on the call.
     """
-    with state.lock:
-      call = state.channel.create_call(
-          parent, flags, state.completion_queue, method, host, deadline)
-      if state.managed_calls is None:
-        state.managed_calls = set((call,))
-        _run_channel_spin_thread(state)
-      else:
-        state.managed_calls.add(call)
-      return call
-  return create_channel_managed_call
+    call = state.channel.create_call(
+        parent, flags, state.completion_queue, method, host, deadline)
+
+    def drive():
+      with state.lock:
+        if state.managed_calls is None:
+          state.managed_calls = set((call,))
+          _run_channel_spin_thread(state)
+        else:
+          state.managed_calls.add(call)
+
+    return call, drive
+  return create
 
 
 class _ChannelConnectivityState(object):
@@ -847,6 +886,7 @@
 
 
 class Channel(grpc.Channel):
+  """A cygrpc.Channel-backed implementation of grpc.Channel."""
 
   def __init__(self, target, options, credentials):
     """Constructor.
@@ -871,25 +911,25 @@
   def unary_unary(
       self, method, request_serializer=None, response_deserializer=None):
     return _UnaryUnaryMultiCallable(
-        self._channel, _create_channel_managed_call(self._call_state),
+        self._channel, _channel_managed_call_management(self._call_state),
         _common.encode(method), request_serializer, response_deserializer)
 
   def unary_stream(
       self, method, request_serializer=None, response_deserializer=None):
     return _UnaryStreamMultiCallable(
-        self._channel, _create_channel_managed_call(self._call_state),
+        self._channel, _channel_managed_call_management(self._call_state),
         _common.encode(method), request_serializer, response_deserializer)
 
   def stream_unary(
       self, method, request_serializer=None, response_deserializer=None):
     return _StreamUnaryMultiCallable(
-        self._channel, _create_channel_managed_call(self._call_state),
+        self._channel, _channel_managed_call_management(self._call_state),
         _common.encode(method), request_serializer, response_deserializer)
 
   def stream_stream(
       self, method, request_serializer=None, response_deserializer=None):
     return _StreamStreamMultiCallable(
-        self._channel, _create_channel_managed_call(self._call_state),
+        self._channel, _channel_managed_call_management(self._call_state),
         _common.encode(method), request_serializer, response_deserializer)
 
   def __del__(self):
diff --git a/src/python/grpcio_tests/tests/http2/_negative_http2_client.py b/src/python/grpcio_tests/tests/http2/_negative_http2_client.py
new file mode 100644
index 0000000..f860468
--- /dev/null
+++ b/src/python/grpcio_tests/tests/http2/_negative_http2_client.py
@@ -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.
+
+"""The Python client used to test negative http2 conditions."""
+
+import argparse
+
+import grpc
+from src.proto.grpc.testing import test_pb2
+from src.proto.grpc.testing import messages_pb2
+
+def _validate_payload_type_and_length(response, expected_type, expected_length):
+  if response.payload.type is not expected_type:
+    raise ValueError(
+      'expected payload type %s, got %s' %
+          (expected_type, type(response.payload.type)))
+  elif len(response.payload.body) != expected_length:
+    raise ValueError(
+      'expected payload body size %d, got %d' %
+          (expected_length, len(response.payload.body)))
+
+def _expect_status_code(call, expected_code):
+  if call.code() != expected_code:
+    raise ValueError(
+      'expected code %s, got %s' % (expected_code, call.code()))
+
+def _expect_status_details(call, expected_details):
+  if call.details() != expected_details:
+    raise ValueError(
+      'expected message %s, got %s' % (expected_details, call.details()))
+
+def _validate_status_code_and_details(call, expected_code, expected_details):
+  _expect_status_code(call, expected_code)
+  _expect_status_details(call, expected_details)
+
+# common requests
+_REQUEST_SIZE = 314159
+_RESPONSE_SIZE = 271828
+
+_SIMPLE_REQUEST = messages_pb2.SimpleRequest(
+    response_type=messages_pb2.COMPRESSABLE,
+    response_size=_RESPONSE_SIZE,
+    payload=messages_pb2.Payload(body=b'\x00' * _REQUEST_SIZE))
+
+def _goaway(stub):
+  first_response = stub.UnaryCall(_SIMPLE_REQUEST)
+  _validate_payload_type_and_length(first_response, 
+      messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+  second_response = stub.UnaryCall(_SIMPLE_REQUEST)
+  _validate_payload_type_and_length(second_response, 
+      messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+
+def _rst_after_header(stub):
+  resp_future = stub.UnaryCall.future(_SIMPLE_REQUEST)
+  _validate_status_code_and_details(resp_future, grpc.StatusCode.UNAVAILABLE, "")
+
+def _rst_during_data(stub):
+  resp_future = stub.UnaryCall.future(_SIMPLE_REQUEST)
+  _validate_status_code_and_details(resp_future, grpc.StatusCode.UNKNOWN, "")
+
+def _rst_after_data(stub):
+  resp_future = stub.UnaryCall.future(_SIMPLE_REQUEST)
+  _validate_payload_type_and_length(next(resp_future),
+      messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+  _validate_status_code_and_details(resp_future, grpc.StatusCode.UNKNOWN, "")
+
+def _ping(stub):
+  response = stub.UnaryCall(_SIMPLE_REQUEST)
+  _validate_payload_type_and_length(response, 
+      messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+
+def _max_streams(stub):
+  # send one req to ensure server sets MAX_STREAMS
+  response = stub.UnaryCall(_SIMPLE_REQUEST)
+  _validate_payload_type_and_length(response, 
+      messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+
+  # give the streams a workout
+  futures = []
+  for _ in range(15):
+    futures.append(stub.UnaryCall.future(_SIMPLE_REQUEST))
+  for future in futures:
+    _validate_payload_type_and_length(future.result(),
+        messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+
+def _run_test_case(test_case, stub):
+  if test_case == 'goaway':
+    _goaway(stub)
+  elif test_case == 'rst_after_header':
+    _rst_after_header(stub)
+  elif test_case == 'rst_during_data':
+    _rst_during_data(stub)
+  elif test_case == 'rst_after_data':
+    _rst_after_data(stub)
+  elif test_case =='ping':
+    _ping(stub)
+  elif test_case == 'max_streams':
+    _max_streams(stub)
+  else:
+    raise ValueError("Invalid test case: %s" % test_case)
+
+def _args():
+  parser = argparse.ArgumentParser()
+  parser.add_argument(
+      '--server_host', help='the host to which to connect', type=str,
+      default="127.0.0.1")
+  parser.add_argument(
+      '--server_port', help='the port to which to connect', type=int,
+      default="8080")
+  parser.add_argument(
+      '--test_case', help='the test case to execute', type=str,
+      default="goaway")
+  return parser.parse_args()
+
+def _stub(server_host, server_port):
+  target = '{}:{}'.format(server_host, server_port)
+  channel = grpc.insecure_channel(target)
+  return test_pb2.TestServiceStub(channel)
+
+def main():
+  args = _args()
+  stub = _stub(args.server_host, args.server_port)
+  _run_test_case(args.test_case, stub)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/src/python/grpcio_tests/tests/interop/methods.py b/src/python/grpcio_tests/tests/interop/methods.py
index 52e56f3..9038ae5 100644
--- a/src/python/grpcio_tests/tests/interop/methods.py
+++ b/src/python/grpcio_tests/tests/interop/methods.py
@@ -33,7 +33,6 @@
 import json
 import os
 import threading
-import time
 
 from oauth2client import client as oauth2client_client
 
@@ -196,16 +195,6 @@
         response, messages_pb2.COMPRESSABLE, sizes[index])
 
 
-def _cancel_after_begin(stub):
-  sizes = (27182, 8, 1828, 45904,)
-  payloads = (messages_pb2.Payload(body=b'\x00' * size) for size in sizes)
-  requests = (messages_pb2.StreamingInputCallRequest(payload=payload)
-              for payload in payloads)
-  response_future = stub.StreamingInputCall.future(requests)
-  response_future.cancel()
-  if not response_future.cancelled():
-    raise ValueError('expected call to be cancelled')
-
 
 class _Pipe(object):
 
@@ -265,6 +254,16 @@
           response, messages_pb2.COMPRESSABLE, response_size)
 
 
+def _cancel_after_begin(stub):
+  with _Pipe() as pipe:
+    response_future = stub.StreamingInputCall.future(pipe)
+    response_future.cancel()
+    if not response_future.cancelled():
+      raise ValueError('expected cancelled method to return True')
+    if response_future.code() is not grpc.StatusCode.CANCELLED:
+      raise ValueError('expected status code CANCELLED')
+
+
 def _cancel_after_first_response(stub):
   request_response_sizes = (31415, 9, 2653, 58979,)
   request_payload_sizes = (27182, 8, 1828, 45904,)
@@ -302,7 +301,6 @@
         response_type=messages_pb2.COMPRESSABLE,
         payload=messages_pb2.Payload(body=b'\x00' * request_payload_size))
     pipe.add(request)
-    time.sleep(0.1)
     try:
       next(response_iterator)
     except grpc.RpcError as rpc_error:
diff --git a/src/python/grpcio_tests/tests/tests.json b/src/python/grpcio_tests/tests/tests.json
index dd4a025..c31a5f9 100644
--- a/src/python/grpcio_tests/tests/tests.json
+++ b/src/python/grpcio_tests/tests/tests.json
@@ -27,6 +27,7 @@
   "unit._cython.cygrpc_test.TypeSmokeTest",
   "unit._empty_message_test.EmptyMessageTest",
   "unit._exit_test.ExitTest",
+  "unit._invalid_metadata_test.InvalidMetadataTest",
   "unit._metadata_code_details_test.MetadataCodeDetailsTest",
   "unit._metadata_test.MetadataTest",
   "unit._rpc_test.RPCTest",
diff --git a/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py b/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py
index e0a7d15..46a964d 100644
--- a/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py
+++ b/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py
@@ -64,7 +64,7 @@
     ready_future = grpc.channel_ready_future(channel)
     ready_future.add_done_callback(callback.accept_value)
     with self.assertRaises(grpc.FutureTimeoutError):
-      ready_future.result(test_constants.SHORT_TIMEOUT)
+      ready_future.result(timeout=test_constants.SHORT_TIMEOUT)
     self.assertFalse(ready_future.cancelled())
     self.assertFalse(ready_future.done())
     self.assertTrue(ready_future.running())
@@ -85,7 +85,7 @@
 
     ready_future = grpc.channel_ready_future(channel)
     ready_future.add_done_callback(callback.accept_value)
-    self.assertIsNone(ready_future.result(test_constants.SHORT_TIMEOUT))
+    self.assertIsNone(ready_future.result(timeout=test_constants.LONG_TIMEOUT))
     value_passed_to_callback = callback.block_until_called()
     self.assertIs(ready_future, value_passed_to_callback)
     self.assertFalse(ready_future.cancelled())
diff --git a/src/python/grpcio_tests/tests/unit/_invalid_metadata_test.py b/src/python/grpcio_tests/tests/unit/_invalid_metadata_test.py
new file mode 100644
index 0000000..2dc225d
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/_invalid_metadata_test.py
@@ -0,0 +1,175 @@
+# 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.
+
+"""Test of RPCs made against gRPC Python's application-layer API."""
+
+import unittest
+
+import grpc
+
+from tests.unit.framework.common import test_constants
+
+_SERIALIZE_REQUEST = lambda bytestring: bytestring * 2
+_DESERIALIZE_REQUEST = lambda bytestring: bytestring[len(bytestring) // 2:]
+_SERIALIZE_RESPONSE = lambda bytestring: bytestring * 3
+_DESERIALIZE_RESPONSE = lambda bytestring: bytestring[:len(bytestring) // 3]
+
+_UNARY_UNARY = '/test/UnaryUnary'
+_UNARY_STREAM = '/test/UnaryStream'
+_STREAM_UNARY = '/test/StreamUnary'
+_STREAM_STREAM = '/test/StreamStream'
+
+
+def _unary_unary_multi_callable(channel):
+  return channel.unary_unary(_UNARY_UNARY)
+
+
+def _unary_stream_multi_callable(channel):
+  return channel.unary_stream(
+      _UNARY_STREAM,
+      request_serializer=_SERIALIZE_REQUEST,
+      response_deserializer=_DESERIALIZE_RESPONSE)
+
+
+def _stream_unary_multi_callable(channel):
+  return channel.stream_unary(
+      _STREAM_UNARY,
+      request_serializer=_SERIALIZE_REQUEST,
+      response_deserializer=_DESERIALIZE_RESPONSE)
+
+
+def _stream_stream_multi_callable(channel):
+  return channel.stream_stream(_STREAM_STREAM)
+
+
+class InvalidMetadataTest(unittest.TestCase):
+
+  def setUp(self):
+    self._channel = grpc.insecure_channel('localhost:8080')
+    self._unary_unary = _unary_unary_multi_callable(self._channel)
+    self._unary_stream = _unary_stream_multi_callable(self._channel)
+    self._stream_unary = _stream_unary_multi_callable(self._channel)
+    self._stream_stream = _stream_stream_multi_callable(self._channel)
+
+  def testUnaryRequestBlockingUnaryResponse(self):
+    request = b'\x07\x08'
+    metadata = (('InVaLiD', 'UnaryRequestBlockingUnaryResponse'),)
+    expected_error_details = "metadata was invalid: %s" % metadata
+    with self.assertRaises(ValueError) as exception_context:
+      self._unary_unary(request, metadata=metadata)
+    self.assertIn(expected_error_details, str(exception_context.exception))
+
+  def testUnaryRequestBlockingUnaryResponseWithCall(self):
+    request = b'\x07\x08'
+    metadata = (('InVaLiD', 'UnaryRequestBlockingUnaryResponseWithCall'),)
+    expected_error_details = "metadata was invalid: %s" % metadata
+    with self.assertRaises(ValueError) as exception_context:
+      self._unary_unary.with_call(request, metadata=metadata)
+    self.assertIn(expected_error_details, str(exception_context.exception))
+
+  def testUnaryRequestFutureUnaryResponse(self):
+    request = b'\x07\x08'
+    metadata = (('InVaLiD', 'UnaryRequestFutureUnaryResponse'),)
+    expected_error_details = "metadata was invalid: %s" % metadata
+    response_future = self._unary_unary.future(request, metadata=metadata)
+    with self.assertRaises(grpc.RpcError) as exception_context:
+      response_future.result()
+    self.assertEqual(
+        exception_context.exception.details(), expected_error_details)
+    self.assertEqual(
+        exception_context.exception.code(), grpc.StatusCode.INTERNAL)
+    self.assertEqual(response_future.details(), expected_error_details)
+    self.assertEqual(response_future.code(), grpc.StatusCode.INTERNAL)
+
+  def testUnaryRequestStreamResponse(self):
+    request = b'\x37\x58'
+    metadata = (('InVaLiD', 'UnaryRequestStreamResponse'),)
+    expected_error_details = "metadata was invalid: %s" % metadata
+    response_iterator = self._unary_stream(request, metadata=metadata)
+    with self.assertRaises(grpc.RpcError) as exception_context:
+      next(response_iterator)
+    self.assertEqual(
+        exception_context.exception.details(), expected_error_details)
+    self.assertEqual(
+        exception_context.exception.code(), grpc.StatusCode.INTERNAL)
+    self.assertEqual(response_iterator.details(), expected_error_details)
+    self.assertEqual(response_iterator.code(), grpc.StatusCode.INTERNAL)
+
+  def testStreamRequestBlockingUnaryResponse(self):
+    request_iterator = (b'\x07\x08' for _ in range(test_constants.STREAM_LENGTH))
+    metadata = (('InVaLiD', 'StreamRequestBlockingUnaryResponse'),)
+    expected_error_details = "metadata was invalid: %s" % metadata
+    with self.assertRaises(ValueError) as exception_context:
+      self._stream_unary(request_iterator, metadata=metadata)
+    self.assertIn(expected_error_details, str(exception_context.exception))
+
+  def testStreamRequestBlockingUnaryResponseWithCall(self):
+    request_iterator = (
+        b'\x07\x08' for _ in range(test_constants.STREAM_LENGTH))
+    metadata = (('InVaLiD', 'StreamRequestBlockingUnaryResponseWithCall'),)
+    expected_error_details = "metadata was invalid: %s" % metadata
+    multi_callable = _stream_unary_multi_callable(self._channel)
+    with self.assertRaises(ValueError) as exception_context:
+      multi_callable.with_call(request_iterator, metadata=metadata)
+    self.assertIn(expected_error_details, str(exception_context.exception))
+
+  def testStreamRequestFutureUnaryResponse(self):
+    request_iterator = (
+        b'\x07\x08' for _ in range(test_constants.STREAM_LENGTH))
+    metadata = (('InVaLiD', 'StreamRequestFutureUnaryResponse'),)
+    expected_error_details = "metadata was invalid: %s" % metadata
+    response_future = self._stream_unary.future(
+        request_iterator, metadata=metadata)
+    with self.assertRaises(grpc.RpcError) as exception_context:
+      response_future.result()
+    self.assertEqual(
+        exception_context.exception.details(), expected_error_details)
+    self.assertEqual(
+        exception_context.exception.code(), grpc.StatusCode.INTERNAL)
+    self.assertEqual(response_future.details(), expected_error_details)
+    self.assertEqual(response_future.code(), grpc.StatusCode.INTERNAL)
+
+  def testStreamRequestStreamResponse(self):
+    request_iterator = (
+        b'\x07\x08' for _ in range(test_constants.STREAM_LENGTH))
+    metadata = (('InVaLiD', 'StreamRequestStreamResponse'),)
+    expected_error_details = "metadata was invalid: %s" % metadata
+    response_iterator = self._stream_stream(request_iterator, metadata=metadata)
+    with self.assertRaises(grpc.RpcError) as exception_context:
+      next(response_iterator)
+    self.assertEqual(
+        exception_context.exception.details(), expected_error_details)
+    self.assertEqual(
+        exception_context.exception.code(), grpc.StatusCode.INTERNAL)
+    self.assertEqual(response_iterator.details(), expected_error_details)
+    self.assertEqual(response_iterator.code(), grpc.StatusCode.INTERNAL)
+
+
+if __name__ == '__main__':
+  unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/beta/_utilities_test.py b/src/python/grpcio_tests/tests/unit/beta/_utilities_test.py
index 90fe10c..9cce96c 100644
--- a/src/python/grpcio_tests/tests/unit/beta/_utilities_test.py
+++ b/src/python/grpcio_tests/tests/unit/beta/_utilities_test.py
@@ -66,7 +66,7 @@
     ready_future = utilities.channel_ready_future(channel)
     ready_future.add_done_callback(callback.accept_value)
     with self.assertRaises(future.TimeoutError):
-      ready_future.result(test_constants.SHORT_TIMEOUT)
+      ready_future.result(timeout=test_constants.SHORT_TIMEOUT)
     self.assertFalse(ready_future.cancelled())
     self.assertFalse(ready_future.done())
     self.assertTrue(ready_future.running())
@@ -88,7 +88,7 @@
       ready_future = utilities.channel_ready_future(channel)
       ready_future.add_done_callback(callback.accept_value)
       self.assertIsNone(
-          ready_future.result(test_constants.SHORT_TIMEOUT))
+          ready_future.result(timeout=test_constants.LONG_TIMEOUT))
       value_passed_to_callback = callback.block_until_called()
       self.assertIs(ready_future, value_passed_to_callback)
       self.assertFalse(ready_future.cancelled())
diff --git a/src/ruby/ext/grpc/rb_byte_buffer.c b/src/ruby/ext/grpc/rb_byte_buffer.c
index f97890e..47fd6d9 100644
--- a/src/ruby/ext/grpc/rb_byte_buffer.c
+++ b/src/ruby/ext/grpc/rb_byte_buffer.c
@@ -65,5 +65,6 @@
                GRPC_SLICE_LENGTH(next));
     grpc_slice_unref(next);
   }
+  grpc_byte_buffer_reader_destroy(&reader);
   return rb_string;
 }
diff --git a/src/ruby/ext/grpc/rb_server.c b/src/ruby/ext/grpc/rb_server.c
index 2a6a246..c7b112c 100644
--- a/src/ruby/ext/grpc/rb_server.c
+++ b/src/ruby/ext/grpc/rb_server.c
@@ -37,6 +37,7 @@
 #include "rb_server.h"
 
 #include <grpc/grpc.h>
+#include <grpc/support/atm.h>
 #include <grpc/grpc_security.h>
 #include <grpc/support/log.h>
 #include "rb_call.h"
@@ -59,22 +60,26 @@
   /* The actual server */
   grpc_server *wrapped;
   grpc_completion_queue *queue;
+  gpr_atm shutdown_started;
 } grpc_rb_server;
 
 static void destroy_server(grpc_rb_server *server, gpr_timespec deadline) {
   grpc_event ev;
-  if (server->wrapped != NULL) {
-    grpc_server_shutdown_and_notify(server->wrapped, server->queue, NULL);
-    ev = rb_completion_queue_pluck(server->queue, NULL, deadline, NULL);
-    if (ev.type == GRPC_QUEUE_TIMEOUT) {
-      grpc_server_cancel_all_calls(server->wrapped);
-      rb_completion_queue_pluck(server->queue, NULL,
-                                gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
+  // This can be started by app or implicitly by GC. Avoid a race between these.
+  if (gpr_atm_full_fetch_add(&server->shutdown_started, (gpr_atm)1) == 0) {
+    if (server->wrapped != NULL) {
+      grpc_server_shutdown_and_notify(server->wrapped, server->queue, NULL);
+      ev = rb_completion_queue_pluck(server->queue, NULL, deadline, NULL);
+      if (ev.type == GRPC_QUEUE_TIMEOUT) {
+        grpc_server_cancel_all_calls(server->wrapped);
+        rb_completion_queue_pluck(server->queue, NULL,
+                                  gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
+      }
+      grpc_server_destroy(server->wrapped);
+      grpc_rb_completion_queue_destroy(server->queue);
+      server->wrapped = NULL;
+      server->queue = NULL;
     }
-    grpc_server_destroy(server->wrapped);
-    grpc_rb_completion_queue_destroy(server->queue);
-    server->wrapped = NULL;
-    server->queue = NULL;
   }
 }
 
@@ -115,6 +120,7 @@
 static VALUE grpc_rb_server_alloc(VALUE cls) {
   grpc_rb_server *wrapper = ALLOC(grpc_rb_server);
   wrapper->wrapped = NULL;
+  wrapper->shutdown_started = (gpr_atm)0;
   return TypedData_Wrap_Struct(cls, &grpc_rb_server_data_type, wrapper);
 }
 
diff --git a/src/ruby/lib/grpc/errors.rb b/src/ruby/lib/grpc/errors.rb
index 23b2bb7..f6998e1 100644
--- a/src/ruby/lib/grpc/errors.rb
+++ b/src/ruby/lib/grpc/errors.rb
@@ -35,9 +35,18 @@
   # either end of a GRPC connection.  When raised, it indicates that a status
   # error should be returned to the other end of a GRPC connection; when
   # caught it means that this end received a status error.
+  #
+  # There is also subclass of BadStatus in this module for each GRPC status.
+  # E.g., the GRPC::Cancelled class corresponds to status CANCELLED.
+  #
+  # See
+  # https://github.com/grpc/grpc/blob/master/include/grpc/impl/codegen/status.h
+  # for detailed descriptions of each status code.
   class BadStatus < StandardError
     attr_reader :code, :details, :metadata
 
+    include GRPC::Core::StatusCodes
+
     # @param code [Numeric] the status code
     # @param details [String] the details of the exception
     # @param metadata [Hash] the error's metadata
@@ -55,9 +64,152 @@
     def to_status
       Struct::Status.new(code, details, @metadata)
     end
+
+    def self.new_status_exception(code, details = 'unkown cause', metadata = {})
+      codes = {}
+      codes[OK] = Ok
+      codes[CANCELLED] = Cancelled
+      codes[UNKNOWN] = Unknown
+      codes[INVALID_ARGUMENT] = InvalidArgument
+      codes[DEADLINE_EXCEEDED] = DeadlineExceeded
+      codes[NOT_FOUND] = NotFound
+      codes[ALREADY_EXISTS] = AlreadyExists
+      codes[PERMISSION_DENIED] =  PermissionDenied
+      codes[UNAUTHENTICATED] = Unauthenticated
+      codes[RESOURCE_EXHAUSTED] = ResourceExhausted
+      codes[FAILED_PRECONDITION] = FailedPrecondition
+      codes[ABORTED] = Aborted
+      codes[OUT_OF_RANGE] = OutOfRange
+      codes[UNIMPLEMENTED] =  Unimplemented
+      codes[INTERNAL] = Internal
+      codes[UNIMPLEMENTED] =  Unimplemented
+      codes[UNAVAILABLE] =  Unavailable
+      codes[DATA_LOSS] = DataLoss
+
+      if codes[code].nil?
+        BadStatus.new(code, details, metadata)
+      else
+        codes[code].new(details, metadata)
+      end
+    end
   end
 
-  # Cancelled is an exception class that indicates that an rpc was cancelled.
-  class Cancelled < StandardError
+  # GRPC status code corresponding to status OK
+  class Ok < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::OK, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status CANCELLED
+  class Cancelled < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::CANCELLED, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status UNKNOWN
+  class Unknown < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::UNKNOWN, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status INVALID_ARGUMENT
+  class InvalidArgument < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::INVALID_ARGUMENT, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status DEADLINE_EXCEEDED
+  class DeadlineExceeded < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::DEADLINE_EXCEEDED, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status NOT_FOUND
+  class NotFound < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::NOT_FOUND, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status ALREADY_EXISTS
+  class AlreadyExists < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::ALREADY_EXISTS, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status PERMISSION_DENIED
+  class PermissionDenied < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::PERMISSION_DENIED, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status UNAUTHENTICATED
+  class Unauthenticated < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::UNAUTHENTICATED, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status RESOURCE_EXHAUSTED
+  class ResourceExhausted < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::RESOURCE_EXHAUSTED, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status FAILED_PRECONDITION
+  class FailedPrecondition < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::FAILED_PRECONDITION, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status ABORTED
+  class Aborted < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::ABORTED, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status OUT_OF_RANGE
+  class OutOfRange < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::OUT_OF_RANGE, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status UNIMPLEMENTED
+  class Unimplemented < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::UNIMPLEMENTED, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status INTERNAL
+  class Internal < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::INTERNAL, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status UNAVAILABLE
+  class Unavailable < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::UNAVAILABLE, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status DATA_LOSS
+  class DataLoss < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::DATA_LOSS, details, metadata)
+    end
   end
 end
diff --git a/src/ruby/lib/grpc/generic/active_call.rb b/src/ruby/lib/grpc/generic/active_call.rb
index f5c426e..3b31f77 100644
--- a/src/ruby/lib/grpc/generic/active_call.rb
+++ b/src/ruby/lib/grpc/generic/active_call.rb
@@ -43,8 +43,8 @@
         GRPC.logger.debug("Failing with status #{status}")
         # raise BadStatus, propagating the metadata if present.
         md = status.metadata
-        fail GRPC::BadStatus.new(status.code, status.details, md),
-             "status code: #{status.code}, details: #{status.details}"
+        fail GRPC::BadStatus.new_status_exception(
+          status.code, status.details, md)
       end
       status
     end
diff --git a/src/ruby/lib/grpc/generic/bidi_call.rb b/src/ruby/lib/grpc/generic/bidi_call.rb
index d7cd9e6..8943f3f 100644
--- a/src/ruby/lib/grpc/generic/bidi_call.rb
+++ b/src/ruby/lib/grpc/generic/bidi_call.rb
@@ -219,6 +219,10 @@
       GRPC.logger.debug('bidi-read-loop: finished')
       @reads_complete = true
       finished
+      # Make sure that the write loop is done done before finishing the call.
+      # Note that blocking is ok at this point because we've already received
+      # a status
+      @enq_th.join if is_client
     end
   end
 end
diff --git a/src/ruby/lib/grpc/generic/service.rb b/src/ruby/lib/grpc/generic/service.rb
index 06ea5b3..84f1ce7 100644
--- a/src/ruby/lib/grpc/generic/service.rb
+++ b/src/ruby/lib/grpc/generic/service.rb
@@ -111,7 +111,8 @@
                                       marshal_class_method,
                                       unmarshal_class_method)
         define_method(GenericService.underscore(name.to_s).to_sym) do
-          fail GRPC::BadStatus, GRPC::Core::StatusCodes::UNIMPLEMENTED
+          fail GRPC::BadStatus.new_status_exception(
+            GRPC::Core::StatusCodes::UNIMPLEMENTED)
         end
       end
 
diff --git a/src/ruby/pb/grpc/health/checker.rb b/src/ruby/pb/grpc/health/checker.rb
index 4bce174..6b2d852 100644
--- a/src/ruby/pb/grpc/health/checker.rb
+++ b/src/ruby/pb/grpc/health/checker.rb
@@ -52,7 +52,9 @@
         @status_mutex.synchronize do
           status = @statuses["#{req.service}"]
         end
-        fail GRPC::BadStatus, StatusCodes::NOT_FOUND if status.nil?
+	if status.nil?
+          fail GRPC::BadStatus.new_status_exception(StatusCodes::NOT_FOUND)
+	end
         HealthCheckResponse.new(status: status)
       end
 
diff --git a/src/ruby/pb/test/client.rb b/src/ruby/pb/test/client.rb
index 1e3ae65..f101f9d 100755
--- a/src/ruby/pb/test/client.rb
+++ b/src/ruby/pb/test/client.rb
@@ -459,11 +459,8 @@
     deadline = GRPC::Core::TimeConsts::from_relative_time(1)
     resps = @stub.full_duplex_call(enum.each_item, deadline: deadline)
     resps.each { } # wait to receive each request (or timeout)
-    fail 'Should have raised GRPC::BadStatus(DEADLINE_EXCEEDED)'
-  rescue GRPC::BadStatus => e
-    assert("#{__callee__}: status was wrong") do
-      e.code == GRPC::Core::StatusCodes::DEADLINE_EXCEEDED
-    end
+    fail 'Should have raised GRPC::DeadlineExceeded'
+  rescue GRPC::DeadlineExceeded
   end
 
   def empty_stream
diff --git a/src/ruby/qps/client.rb b/src/ruby/qps/client.rb
index 8aed866..8171926 100644
--- a/src/ruby/qps/client.rb
+++ b/src/ruby/qps/client.rb
@@ -134,6 +134,7 @@
     resp = stub.streaming_call(q.each_item)
     start = Time.now
     q.push(req)
+    pushed_sentinal = false
     resp.each do |r|
       @histogram.add((Time.now-start)*1e9)
       if !@done
@@ -141,8 +142,9 @@
         start = Time.now
         q.push(req)
       else
-        q.push(self)
-        break
+        q.push(self) unless pushed_sentinal
+	# Continue polling on the responses to consume and release resources
+        pushed_sentinal = true
       end
     end
   end
diff --git a/src/ruby/spec/error_sanity_spec.rb b/src/ruby/spec/error_sanity_spec.rb
new file mode 100644
index 0000000..77e94a8
--- /dev/null
+++ b/src/ruby/spec/error_sanity_spec.rb
@@ -0,0 +1,64 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+require 'grpc'
+
+StatusCodes = GRPC::Core::StatusCodes
+
+describe StatusCodes do
+  # convert upper snake-case to camel case.
+  # e.g., DEADLINE_EXCEEDED -> DeadlineExceeded
+  def upper_snake_to_camel(name)
+    name.to_s.split('_').map(&:downcase).map(&:capitalize).join('')
+  end
+
+  StatusCodes.constants.each do |status_name|
+    it 'there is a subclass of BadStatus corresponding to StatusCode: ' \
+      "#{status_name} that has code: #{StatusCodes.const_get(status_name)}" do
+      camel_case = upper_snake_to_camel(status_name)
+      error_class = GRPC.const_get(camel_case)
+      # expect the error class to be a subclass of BadStatus
+      expect(error_class < GRPC::BadStatus)
+
+      error_object = error_class.new
+      # check that the code matches the int value of the error's constant
+      status_code = StatusCodes.const_get(status_name)
+      expect(error_object.code).to eq(status_code)
+
+      # check default parameters
+      expect(error_object.details).to eq('unknown cause')
+      expect(error_object.metadata).to eq({})
+
+      # check that the BadStatus factory for creates the correct
+      # exception too
+      from_factory = GRPC::BadStatus.new_status_exception(status_code)
+      expect(from_factory.is_a?(error_class)).to be(true)
+    end
+  end
+end
diff --git a/src/ruby/spec/generic/client_stub_spec.rb b/src/ruby/spec/generic/client_stub_spec.rb
index 607a4a3..b51b291 100644
--- a/src/ruby/spec/generic/client_stub_spec.rb
+++ b/src/ruby/spec/generic/client_stub_spec.rb
@@ -190,15 +190,14 @@
         end
         creds = GRPC::Core::CallCredentials.new(failing_auth)
 
-        error_occured = false
+        unauth_error_occured = false
         begin
           get_response(stub, credentials: creds)
-        rescue GRPC::BadStatus => e
-          error_occured = true
-          expect(e.code).to eq(GRPC::Core::StatusCodes::UNAUTHENTICATED)
+        rescue GRPC::Unauthenticated => e
+          unauth_error_occured = true
           expect(e.details.include?(error_message)).to be true
         end
-        expect(error_occured).to eq(true)
+        expect(unauth_error_occured).to eq(true)
 
         # Kill the server thread so tests can complete
         th.kill
diff --git a/src/ruby/spec/generic/rpc_server_spec.rb b/src/ruby/spec/generic/rpc_server_spec.rb
index c569479..806ea8c 100644
--- a/src/ruby/spec/generic/rpc_server_spec.rb
+++ b/src/ruby/spec/generic/rpc_server_spec.rb
@@ -408,21 +408,21 @@
         req = EchoMsg.new
         n = 20 # arbitrary, use as many to ensure the server pool is exceeded
         threads = []
-        bad_status_code = nil
+        one_failed_as_unavailable = false
         n.times do
           threads << Thread.new do
             stub = SlowStub.new(alt_host, :this_channel_is_insecure)
             begin
               stub.an_rpc(req)
-            rescue GRPC::BadStatus => e
-              bad_status_code = e.code
+            rescue GRPC::ResourceExhausted
+              one_failed_as_unavailable = true
             end
           end
         end
         threads.each(&:join)
         alt_srv.stop
         t.join
-        expect(bad_status_code).to be(StatusCodes::RESOURCE_EXHAUSTED)
+        expect(one_failed_as_unavailable).to be(true)
       end
     end
 
diff --git a/src/ruby/spec/pb/health/checker_spec.rb b/src/ruby/spec/pb/health/checker_spec.rb
index 4711e09..7195100 100644
--- a/src/ruby/spec/pb/health/checker_spec.rb
+++ b/src/ruby/spec/pb/health/checker_spec.rb
@@ -122,7 +122,7 @@
           checker.check(HCReq.new(service: t[:service]), nil)
         end
         expected_msg = /#{StatusCodes::NOT_FOUND}/
-        expect(&blk).to raise_error GRPC::BadStatus, expected_msg
+        expect(&blk).to raise_error GRPC::NotFound, expected_msg
       end
     end
   end
@@ -141,7 +141,7 @@
           checker.check(HCReq.new(service: t[:service]), nil)
         end
         expected_msg = /#{StatusCodes::NOT_FOUND}/
-        expect(&blk).to raise_error GRPC::BadStatus, expected_msg
+        expect(&blk).to raise_error GRPC::NotFound, expected_msg
       end
     end
   end
@@ -163,7 +163,7 @@
           checker.check(HCReq.new(service: t[:service]), nil)
         end
         expected_msg = /#{StatusCodes::NOT_FOUND}/
-        expect(&blk).to raise_error GRPC::BadStatus, expected_msg
+        expect(&blk).to raise_error GRPC::NotFound, expected_msg
       end
     end
   end
@@ -214,7 +214,7 @@
         stub.check(HCReq.new(service: 'unknown'))
       end
       expected_msg = /#{StatusCodes::NOT_FOUND}/
-      expect(&blk).to raise_error GRPC::BadStatus, expected_msg
+      expect(&blk).to raise_error GRPC::NotFound, expected_msg
       @srv.stop
       t.join
     end
diff --git a/src/ruby/spec/spec_helper.rb b/src/ruby/spec/spec_helper.rb
index c891c1b..c2be0af 100644
--- a/src/ruby/spec/spec_helper.rb
+++ b/src/ruby/spec/spec_helper.rb
@@ -67,3 +67,5 @@
 end
 
 RSpec::Expectations.configuration.warn_about_potential_false_positives = false
+
+Thread.abort_on_exception = true
diff --git a/templates/gRPC-Core.podspec.template b/templates/gRPC-Core.podspec.template
index fbad1a3..1b97d18 100644
--- a/templates/gRPC-Core.podspec.template
+++ b/templates/gRPC-Core.podspec.template
@@ -62,7 +62,7 @@
   %>
   Pod::Spec.new do |s|
     s.name     = 'gRPC-Core'
-    version = '1.0.1'
+    version = '1.0.2'
     s.version  = version
     s.summary  = 'Core cross-platform gRPC library, written in C'
     s.homepage = 'http://www.grpc.io'
@@ -71,7 +71,9 @@
 
     s.source = {
       :git => 'https://github.com/grpc/grpc.git',
-      :tag => "v#{version}",
+      # TODO(mxyan): Change back to "v#{version}" for next release
+      #:tag => "v#{version}",
+      :tag => "objective-c-v#{version}",
       # TODO(jcanizales): Depend explicitly on the nanopb pod, and disable submodules.
       :submodules => true,
     }
diff --git a/templates/grpc.gemspec.template b/templates/grpc.gemspec.template
index 62d61b7..82fbb69 100644
--- a/templates/grpc.gemspec.template
+++ b/templates/grpc.gemspec.template
@@ -29,7 +29,7 @@
     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.2'
+    s.add_dependency 'google-protobuf', '~> 3.1.0'
     s.add_dependency 'googleauth',      '~> 0.5.1'
 
     s.add_development_dependency 'bundler',            '~> 1.9'
diff --git a/test/core/client_channel/lb_policies_test.c b/test/core/client_channel/lb_policies_test.c
index 6e4058f..0166107 100644
--- a/test/core/client_channel/lb_policies_test.c
+++ b/test/core/client_channel/lb_policies_test.c
@@ -241,6 +241,8 @@
   res.n = n;
   res.connections = gpr_malloc(sizeof(*res.connections) * n);
   res.connectivity_states = gpr_malloc(sizeof(*res.connectivity_states) * n);
+  memset(res.connections, 0, sizeof(*res.connections) * n);
+  memset(res.connectivity_states, 0, sizeof(*res.connectivity_states) * n);
   return res;
 }
 
@@ -782,17 +784,15 @@
     }
   }
 
-  /* no server is ever available. The persistent state is TRANSIENT_FAILURE. May
-   * also be CONNECTING if, under load, this check took too long to run and some
-   * subchannel already transitioned to retrying. */
+  /* No server is ever available. There should be no READY states (or SHUTDOWN).
+   * Note that all other states (IDLE, CONNECTING, TRANSIENT_FAILURE) are still
+   * possible, as the policy transitions while attempting to reconnect. */
   for (size_t i = 0; i < sequences->n; i++) {
     const grpc_connectivity_state actual = sequences->connectivity_states[i];
-    if (actual != GRPC_CHANNEL_TRANSIENT_FAILURE &&
-        actual != GRPC_CHANNEL_CONNECTING) {
+    if (actual == GRPC_CHANNEL_READY || actual == GRPC_CHANNEL_SHUTDOWN) {
       gpr_log(GPR_ERROR,
-              "CONNECTIVITY STATUS SEQUENCE FAILURE: expected "
-              "GRPC_CHANNEL_TRANSIENT_FAILURE or GRPC_CHANNEL_CONNECTING, got "
-              "'%s' at iteration #%d",
+              "CONNECTIVITY STATUS SEQUENCE FAILURE: got unexpected state "
+              "'%s' at iteration #%d.",
               grpc_connectivity_state_name(actual), (int)i);
       abort();
     }
@@ -841,17 +841,15 @@
     abort();
   }
 
-  /* ... and that the last one should be TRANSIENT_FAILURE, after all servers
-   * are gone. May also be CONNECTING if, under load, this check took too long
-   * to run and the subchannel already transitioned to retrying. */
+  /* ... and that the last one shouldn't be READY (or SHUTDOWN): all servers are
+   * gone. It may be all other states (IDLE, CONNECTING, TRANSIENT_FAILURE), as
+   * the policy transitions while attempting to reconnect. */
   actual = sequences->connectivity_states[num_iters - 1];
   for (i = 0; i < sequences->n; i++) {
-    if (actual != GRPC_CHANNEL_TRANSIENT_FAILURE &&
-        actual != GRPC_CHANNEL_CONNECTING) {
+    if (actual == GRPC_CHANNEL_READY || actual == GRPC_CHANNEL_SHUTDOWN) {
       gpr_log(GPR_ERROR,
-              "CONNECTIVITY STATUS SEQUENCE FAILURE: expected "
-              "GRPC_CHANNEL_TRANSIENT_FAILURE or GRPC_CHANNEL_CONNECTING, got "
-              "'%s' at iteration #%d",
+              "CONNECTIVITY STATUS SEQUENCE FAILURE: got unexpected state "
+              "'%s' at iteration #%d.",
               grpc_connectivity_state_name(actual), (int)i);
       abort();
     }
@@ -948,8 +946,8 @@
   const size_t NUM_ITERS = 10;
   const size_t NUM_SERVERS = 4;
 
-  grpc_test_init(argc, argv);
   grpc_init();
+  grpc_test_init(argc, argv);
   grpc_tracer_set_enabled("round_robin", 1);
 
   GPR_ASSERT(grpc_lb_policy_create(&exec_ctx, "this-lb-policy-does-not-exist",
diff --git a/test/core/client_channel/resolvers/dns_resolver_connectivity_test.c b/test/core/client_channel/resolvers/dns_resolver_connectivity_test.c
index ffa167a..b421720 100644
--- a/test/core/client_channel/resolvers/dns_resolver_connectivity_test.c
+++ b/test/core/client_channel/resolvers/dns_resolver_connectivity_test.c
@@ -63,7 +63,8 @@
   }
 }
 
-static grpc_resolver *create_resolver(const char *name) {
+static grpc_resolver *create_resolver(grpc_exec_ctx *exec_ctx,
+                                      const char *name) {
   grpc_resolver_factory *factory = grpc_resolver_factory_lookup("dns");
   grpc_uri *uri = grpc_uri_parse(name, 0);
   GPR_ASSERT(uri);
@@ -71,7 +72,7 @@
   memset(&args, 0, sizeof(args));
   args.uri = uri;
   grpc_resolver *resolver =
-      grpc_resolver_factory_create_resolver(factory, &args);
+      grpc_resolver_factory_create_resolver(exec_ctx, factory, &args);
   grpc_resolver_factory_unref(factory);
   grpc_uri_destroy(uri);
   return resolver;
@@ -101,12 +102,10 @@
   grpc_init();
   gpr_mu_init(&g_mu);
   grpc_blocking_resolve_address = my_resolve_address;
-
-  grpc_resolver *resolver = create_resolver("dns:test");
-
   grpc_channel_args *result = (grpc_channel_args *)1;
 
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_resolver *resolver = create_resolver(&exec_ctx, "dns:test");
   gpr_event ev1;
   gpr_event_init(&ev1);
   grpc_resolver_next(&exec_ctx, resolver, &result,
diff --git a/test/core/client_channel/resolvers/dns_resolver_test.c b/test/core/client_channel/resolvers/dns_resolver_test.c
index 41a9125..5603a57 100644
--- a/test/core/client_channel/resolvers/dns_resolver_test.c
+++ b/test/core/client_channel/resolvers/dns_resolver_test.c
@@ -48,7 +48,7 @@
   GPR_ASSERT(uri);
   memset(&args, 0, sizeof(args));
   args.uri = uri;
-  resolver = grpc_resolver_factory_create_resolver(factory, &args);
+  resolver = grpc_resolver_factory_create_resolver(&exec_ctx, factory, &args);
   GPR_ASSERT(resolver != NULL);
   GRPC_RESOLVER_UNREF(&exec_ctx, resolver, "test_succeeds");
   grpc_uri_destroy(uri);
@@ -65,7 +65,7 @@
   GPR_ASSERT(uri);
   memset(&args, 0, sizeof(args));
   args.uri = uri;
-  resolver = grpc_resolver_factory_create_resolver(factory, &args);
+  resolver = grpc_resolver_factory_create_resolver(&exec_ctx, factory, &args);
   GPR_ASSERT(resolver == NULL);
   grpc_uri_destroy(uri);
   grpc_exec_ctx_finish(&exec_ctx);
diff --git a/test/core/client_channel/resolvers/sockaddr_resolver_test.c b/test/core/client_channel/resolvers/sockaddr_resolver_test.c
index 5ef248f..a9fd85a 100644
--- a/test/core/client_channel/resolvers/sockaddr_resolver_test.c
+++ b/test/core/client_channel/resolvers/sockaddr_resolver_test.c
@@ -62,7 +62,7 @@
   GPR_ASSERT(uri);
   memset(&args, 0, sizeof(args));
   args.uri = uri;
-  resolver = grpc_resolver_factory_create_resolver(factory, &args);
+  resolver = grpc_resolver_factory_create_resolver(&exec_ctx, factory, &args);
   GPR_ASSERT(resolver != NULL);
 
   on_resolution_arg on_res_arg;
@@ -88,7 +88,7 @@
   GPR_ASSERT(uri);
   memset(&args, 0, sizeof(args));
   args.uri = uri;
-  resolver = grpc_resolver_factory_create_resolver(factory, &args);
+  resolver = grpc_resolver_factory_create_resolver(&exec_ctx, factory, &args);
   GPR_ASSERT(resolver == NULL);
   grpc_uri_destroy(uri);
   grpc_exec_ctx_finish(&exec_ctx);
diff --git a/test/core/end2end/fake_resolver.c b/test/core/end2end/fake_resolver.c
index 7380dcc..ed85030 100644
--- a/test/core/end2end/fake_resolver.c
+++ b/test/core/end2end/fake_resolver.c
@@ -140,7 +140,8 @@
 
 static void do_nothing(void* ignored) {}
 
-static grpc_resolver* fake_resolver_create(grpc_resolver_factory* factory,
+static grpc_resolver* fake_resolver_create(grpc_exec_ctx* exec_ctx,
+                                           grpc_resolver_factory* factory,
                                            grpc_resolver_args* args) {
   if (0 != strcmp(args->uri->authority, "")) {
     gpr_log(GPR_ERROR, "authority based uri's not supported by the %s scheme",
diff --git a/test/core/end2end/fuzzers/api_fuzzer.c b/test/core/end2end/fuzzers/api_fuzzer.c
index 19ac6ce..746134c 100644
--- a/test/core/end2end/fuzzers/api_fuzzer.c
+++ b/test/core/end2end/fuzzers/api_fuzzer.c
@@ -361,7 +361,9 @@
 }
 
 void my_resolve_address(grpc_exec_ctx *exec_ctx, const char *addr,
-                        const char *default_port, grpc_closure *on_done,
+                        const char *default_port,
+                        grpc_pollset_set *interested_parties,
+                        grpc_closure *on_done,
                         grpc_resolved_addresses **addresses) {
   addr_req *r = gpr_malloc(sizeof(*r));
   r->addr = gpr_strdup(addr);
diff --git a/test/core/iomgr/resolve_address_test.c b/test/core/iomgr/resolve_address_test.c
index 2dd0d88..e4136a7 100644
--- a/test/core/iomgr/resolve_address_test.c
+++ b/test/core/iomgr/resolve_address_test.c
@@ -32,8 +32,10 @@
  */
 
 #include "src/core/lib/iomgr/resolve_address.h"
+#include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/sync.h>
+#include <grpc/support/thd.h>
 #include <grpc/support/time.h>
 #include "src/core/lib/iomgr/executor.h"
 #include "src/core/lib/iomgr/iomgr.h"
@@ -46,16 +48,72 @@
 typedef struct args_struct {
   gpr_event ev;
   grpc_resolved_addresses *addrs;
+  gpr_atm done_atm;
+  gpr_mu *mu;
+  grpc_pollset *pollset;
+  grpc_pollset_set *pollset_set;
 } args_struct;
 
-void args_init(args_struct *args) {
+static void do_nothing(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {}
+
+void args_init(grpc_exec_ctx *exec_ctx, args_struct *args) {
   gpr_event_init(&args->ev);
+  args->pollset = gpr_malloc(grpc_pollset_size());
+  grpc_pollset_init(args->pollset, &args->mu);
+  args->pollset_set = grpc_pollset_set_create();
+  grpc_pollset_set_add_pollset(exec_ctx, args->pollset_set, args->pollset);
   args->addrs = NULL;
 }
 
-void args_finish(args_struct *args) {
+void args_finish(grpc_exec_ctx *exec_ctx, args_struct *args) {
   GPR_ASSERT(gpr_event_wait(&args->ev, test_deadline()));
   grpc_resolved_addresses_destroy(args->addrs);
+  grpc_pollset_set_del_pollset(exec_ctx, args->pollset_set, args->pollset);
+  grpc_pollset_set_destroy(args->pollset_set);
+  grpc_closure do_nothing_cb;
+  grpc_closure_init(&do_nothing_cb, do_nothing, NULL);
+  grpc_pollset_shutdown(exec_ctx, args->pollset, &do_nothing_cb);
+  // exec_ctx needs to be flushed before calling grpc_pollset_destroy()
+  grpc_exec_ctx_flush(exec_ctx);
+  grpc_pollset_destroy(args->pollset);
+  gpr_free(args->pollset);
+}
+
+static gpr_timespec n_sec_deadline(int seconds) {
+  return gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
+                      gpr_time_from_seconds(seconds, GPR_TIMESPAN));
+}
+
+static void actually_poll(void *argsp) {
+  args_struct *args = argsp;
+  gpr_timespec deadline = n_sec_deadline(10);
+  while (true) {
+    bool done = gpr_atm_acq_load(&args->done_atm) != 0;
+    if (done) {
+      break;
+    }
+    gpr_timespec time_left =
+        gpr_time_sub(deadline, gpr_now(GPR_CLOCK_REALTIME));
+    gpr_log(GPR_DEBUG, "done=%d, time_left=%" PRId64 ".%09d", done,
+            time_left.tv_sec, time_left.tv_nsec);
+    GPR_ASSERT(gpr_time_cmp(time_left, gpr_time_0(GPR_TIMESPAN)) >= 0);
+    grpc_pollset_worker *worker = NULL;
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    gpr_mu_lock(args->mu);
+    GRPC_LOG_IF_ERROR(
+        "pollset_work",
+        grpc_pollset_work(&exec_ctx, args->pollset, &worker,
+                          gpr_now(GPR_CLOCK_REALTIME), n_sec_deadline(1)));
+    gpr_mu_unlock(args->mu);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
+  gpr_event_set(&args->ev, (void *)1);
+}
+
+static void poll_pollset_until_request_done(args_struct *args) {
+  gpr_atm_rel_store(&args->done_atm, 0);
+  gpr_thd_id id;
+  gpr_thd_new(&id, actually_poll, args, NULL);
 }
 
 static void must_succeed(grpc_exec_ctx *exec_ctx, void *argsp,
@@ -64,53 +122,57 @@
   GPR_ASSERT(err == GRPC_ERROR_NONE);
   GPR_ASSERT(args->addrs != NULL);
   GPR_ASSERT(args->addrs->naddrs > 0);
-  gpr_event_set(&args->ev, (void *)1);
+  gpr_atm_rel_store(&args->done_atm, 1);
 }
 
 static void must_fail(grpc_exec_ctx *exec_ctx, void *argsp, grpc_error *err) {
   args_struct *args = argsp;
   GPR_ASSERT(err != GRPC_ERROR_NONE);
-  gpr_event_set(&args->ev, (void *)1);
+  gpr_atm_rel_store(&args->done_atm, 1);
 }
 
 static void test_localhost(void) {
-  args_struct args;
-  args_init(&args);
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_resolve_address(&exec_ctx, "localhost:1", NULL,
+  args_struct args;
+  args_init(&exec_ctx, &args);
+  poll_pollset_until_request_done(&args);
+  grpc_resolve_address(&exec_ctx, "localhost:1", NULL, args.pollset_set,
                        grpc_closure_create(must_succeed, &args), &args.addrs);
+  args_finish(&exec_ctx, &args);
   grpc_exec_ctx_finish(&exec_ctx);
-  args_finish(&args);
 }
 
 static void test_default_port(void) {
-  args_struct args;
-  args_init(&args);
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_resolve_address(&exec_ctx, "localhost", "1",
+  args_struct args;
+  args_init(&exec_ctx, &args);
+  poll_pollset_until_request_done(&args);
+  grpc_resolve_address(&exec_ctx, "localhost", "1", args.pollset_set,
                        grpc_closure_create(must_succeed, &args), &args.addrs);
+  args_finish(&exec_ctx, &args);
   grpc_exec_ctx_finish(&exec_ctx);
-  args_finish(&args);
 }
 
 static void test_missing_default_port(void) {
-  args_struct args;
-  args_init(&args);
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_resolve_address(&exec_ctx, "localhost", NULL,
+  args_struct args;
+  args_init(&exec_ctx, &args);
+  poll_pollset_until_request_done(&args);
+  grpc_resolve_address(&exec_ctx, "localhost", NULL, args.pollset_set,
                        grpc_closure_create(must_fail, &args), &args.addrs);
+  args_finish(&exec_ctx, &args);
   grpc_exec_ctx_finish(&exec_ctx);
-  args_finish(&args);
 }
 
 static void test_ipv6_with_port(void) {
-  args_struct args;
-  args_init(&args);
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_resolve_address(&exec_ctx, "[2001:db8::1]:1", NULL,
+  args_struct args;
+  args_init(&exec_ctx, &args);
+  poll_pollset_until_request_done(&args);
+  grpc_resolve_address(&exec_ctx, "[2001:db8::1]:1", NULL, args.pollset_set,
                        grpc_closure_create(must_succeed, &args), &args.addrs);
+  args_finish(&exec_ctx, &args);
   grpc_exec_ctx_finish(&exec_ctx);
-  args_finish(&args);
 }
 
 static void test_ipv6_without_port(void) {
@@ -119,13 +181,14 @@
   };
   unsigned i;
   for (i = 0; i < sizeof(kCases) / sizeof(*kCases); i++) {
-    args_struct args;
-    args_init(&args);
     grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-    grpc_resolve_address(&exec_ctx, kCases[i], "80",
+    args_struct args;
+    args_init(&exec_ctx, &args);
+    poll_pollset_until_request_done(&args);
+    grpc_resolve_address(&exec_ctx, kCases[i], "80", args.pollset_set,
                          grpc_closure_create(must_succeed, &args), &args.addrs);
+    args_finish(&exec_ctx, &args);
     grpc_exec_ctx_finish(&exec_ctx);
-    args_finish(&args);
   }
 }
 
@@ -135,13 +198,14 @@
   };
   unsigned i;
   for (i = 0; i < sizeof(kCases) / sizeof(*kCases); i++) {
-    args_struct args;
-    args_init(&args);
     grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-    grpc_resolve_address(&exec_ctx, kCases[i], NULL,
+    args_struct args;
+    args_init(&exec_ctx, &args);
+    poll_pollset_until_request_done(&args);
+    grpc_resolve_address(&exec_ctx, kCases[i], NULL, args.pollset_set,
                          grpc_closure_create(must_fail, &args), &args.addrs);
+    args_finish(&exec_ctx, &args);
     grpc_exec_ctx_finish(&exec_ctx);
-    args_finish(&args);
   }
 }
 
@@ -151,13 +215,14 @@
   };
   unsigned i;
   for (i = 0; i < sizeof(kCases) / sizeof(*kCases); i++) {
-    args_struct args;
-    args_init(&args);
     grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-    grpc_resolve_address(&exec_ctx, kCases[i], "1",
+    args_struct args;
+    args_init(&exec_ctx, &args);
+    poll_pollset_until_request_done(&args);
+    grpc_resolve_address(&exec_ctx, kCases[i], "1", args.pollset_set,
                          grpc_closure_create(must_fail, &args), &args.addrs);
+    args_finish(&exec_ctx, &args);
     grpc_exec_ctx_finish(&exec_ctx);
-    args_finish(&args);
   }
 }
 
diff --git a/test/cpp/grpclb/grpclb_test.cc b/test/cpp/grpclb/grpclb_test.cc
index fcdcaba..de304b9 100644
--- a/test/cpp/grpclb/grpclb_test.cc
+++ b/test/cpp/grpclb/grpclb_test.cc
@@ -659,7 +659,7 @@
   char *server_uri;
   // The grpclb LB policy will be automatically selected by virtue of
   // the fact that the returned addresses are balancer addresses.
-  gpr_asprintf(&server_uri, "test:%s?lb_enabled=1",
+  gpr_asprintf(&server_uri, "test:///%s?lb_enabled=1",
                tf.lb_server.servers_hostport);
   setup_client(server_uri, &tf.client);
   gpr_free(server_uri);
diff --git a/test/cpp/interop/stress_test.cc b/test/cpp/interop/stress_test.cc
index fc35db52..97e6588 100644
--- a/test/cpp/interop/stress_test.cc
+++ b/test/cpp/interop/stress_test.cc
@@ -371,9 +371,9 @@
   }
 
   // Start metrics server before waiting for the stress test threads
+  std::unique_ptr<grpc::Server> metrics_server;
   if (FLAGS_metrics_port > 0) {
-    std::unique_ptr<grpc::Server> metrics_server =
-        metrics_service.StartServer(FLAGS_metrics_port);
+    metrics_server = metrics_service.StartServer(FLAGS_metrics_port);
   }
 
   // Wait for the stress test threads to complete
diff --git a/test/http2_test/http2_test_server.py b/test/http2_test/http2_test_server.py
index c9b33e0..44e36d3 100644
--- a/test/http2_test/http2_test_server.py
+++ b/test/http2_test/http2_test_server.py
@@ -73,15 +73,18 @@
     else:
       return t().get_base_server()
 
-if __name__ == "__main__":
-  logging.basicConfig(format = "%(levelname) -10s %(asctime)s %(module)s:%(lineno)s | %(message)s", level=logging.INFO)
+if __name__ == '__main__':
+  logging.basicConfig(
+    format='%(levelname) -10s %(asctime)s %(module)s:%(lineno)s | %(message)s',
+    level=logging.INFO)
   parser = argparse.ArgumentParser()
-  parser.add_argument("test")
-  parser.add_argument("port")
+  parser.add_argument('--test_case', choices=sorted(_TEST_CASE_MAPPING.keys()),
+    help='test case to run', required=True)
+  parser.add_argument('--port', type=int, default=8080,
+    help='port to run the server (default: 8080)')
   args = parser.parse_args()
-  if args.test not in _TEST_CASE_MAPPING.keys():
-    logging.error('unknown test: %s' % args.test)
-  else:
-    endpoint = twisted.internet.endpoints.TCP4ServerEndpoint(twisted.internet.reactor, int(args.port), backlog=128)
-    endpoint.listen(H2Factory(args.test))
-    twisted.internet.reactor.run()
+  logging.info('Running test case %s on port %d' % (args.test_case, args.port))
+  endpoint = twisted.internet.endpoints.TCP4ServerEndpoint(
+    twisted.internet.reactor, args.port, backlog=128)
+  endpoint.listen(H2Factory(args.test_case))
+  twisted.internet.reactor.run()
diff --git a/tools/buildgen/generate_build_additions.sh b/tools/buildgen/generate_build_additions.sh
index 53c30c7..a4373ed 100644
--- a/tools/buildgen/generate_build_additions.sh
+++ b/tools/buildgen/generate_build_additions.sh
@@ -28,6 +28,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.
 
+set -e
+
 gen_build_yaml_dirs="  \
   src/boringssl        \
   src/benchmark \
diff --git a/tools/run_tests/build_artifact_node.bat b/tools/run_tests/build_artifact_node.bat
index 57d55ef..2e0ecd2 100644
--- a/tools/run_tests/build_artifact_node.bat
+++ b/tools/run_tests/build_artifact_node.bat
@@ -27,7 +27,7 @@
 @rem (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 @rem OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-set node_versions=0.12.0 1.0.0 1.1.0 2.0.0 3.0.0 4.0.0 5.0.0 6.0.0
+set node_versions=0.12.0 1.0.0 1.1.0 2.0.0 3.0.0 4.0.0 5.0.0 6.0.0 7.0.0
 
 set PATH=%PATH%;C:\Program Files\nodejs\;%APPDATA%\npm
 
diff --git a/tools/run_tests/build_artifact_node.sh b/tools/run_tests/build_artifact_node.sh
index 9d06472..778a5c9 100755
--- a/tools/run_tests/build_artifact_node.sh
+++ b/tools/run_tests/build_artifact_node.sh
@@ -42,7 +42,7 @@
 
 npm update
 
-node_versions=( 0.12.0 1.0.0 1.1.0 2.0.0 3.0.0 4.0.0 5.0.0 6.0.0 )
+node_versions=( 0.12.0 1.0.0 1.1.0 2.0.0 3.0.0 4.0.0 5.0.0 6.0.0 7.0.0 )
 
 for version in ${node_versions[@]}
 do