Merge pull request #3583 from murgatroid99/node_coverage

Node test coverage reporting
diff --git a/include/grpc/support/slice_buffer.h b/include/grpc/support/slice_buffer.h
index f3f14a6..e4f296c 100644
--- a/include/grpc/support/slice_buffer.h
+++ b/include/grpc/support/slice_buffer.h
@@ -87,7 +87,8 @@
 /* move all of the elements of src into dst */
 void gpr_slice_buffer_move_into(gpr_slice_buffer *src, gpr_slice_buffer *dst);
 /* remove n bytes from the end of a slice buffer */
-void gpr_slice_buffer_trim_end(gpr_slice_buffer *src, size_t n, gpr_slice_buffer *garbage);
+void gpr_slice_buffer_trim_end(gpr_slice_buffer *src, size_t n,
+                               gpr_slice_buffer *garbage);
 
 #ifdef __cplusplus
 }
diff --git a/src/core/iomgr/iomgr.c b/src/core/iomgr/iomgr.c
index a103993..e61fc32 100644
--- a/src/core/iomgr/iomgr.c
+++ b/src/core/iomgr/iomgr.c
@@ -51,13 +51,6 @@
 static int g_shutdown;
 static grpc_iomgr_object g_root_object;
 
-void grpc_kick_poller(void) {
-  /* Empty. The background callback executor polls periodically. The activity
-   * the kicker is trying to draw the executor's attention to will be picked up
-   * either by one of the periodic wakeups or by one of the polling application
-   * threads. */
-}
-
 void grpc_iomgr_init(void) {
   g_shutdown = 0;
   gpr_mu_init(&g_mu);
diff --git a/src/core/iomgr/pollset_multipoller_with_epoll.c b/src/core/iomgr/pollset_multipoller_with_epoll.c
index b22eaa6..d26e60f 100644
--- a/src/core/iomgr/pollset_multipoller_with_epoll.c
+++ b/src/core/iomgr/pollset_multipoller_with_epoll.c
@@ -213,11 +213,15 @@
             int cancel = ep_ev[i].events & (EPOLLERR | EPOLLHUP);
             int read_ev = ep_ev[i].events & (EPOLLIN | EPOLLPRI);
             int write_ev = ep_ev[i].events & EPOLLOUT;
-            if (read_ev || cancel) {
-              grpc_fd_become_readable(exec_ctx, fd);
-            }
-            if (write_ev || cancel) {
-              grpc_fd_become_writable(exec_ctx, fd);
+            if (fd == NULL) {
+              grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd);
+            } else {
+              if (read_ev || cancel) {
+                grpc_fd_become_readable(exec_ctx, fd);
+              }
+              if (write_ev || cancel) {
+                grpc_fd_become_writable(exec_ctx, fd);
+              }
             }
           }
         }
@@ -246,6 +250,8 @@
                                      size_t nfds) {
   size_t i;
   pollset_hdr *h = gpr_malloc(sizeof(pollset_hdr));
+  struct epoll_event ev;
+  int err;
 
   pollset->vtable = &multipoll_with_epoll_pollset;
   pollset->data.ptr = h;
@@ -255,6 +261,15 @@
     gpr_log(GPR_ERROR, "epoll_create1 failed: %s", strerror(errno));
     abort();
   }
+
+  ev.events = (uint32_t)(EPOLLIN | EPOLLET);
+  ev.data.ptr = NULL;
+  err = epoll_ctl(h->epoll_fd, EPOLL_CTL_ADD, GRPC_WAKEUP_FD_GET_READ_FD(&grpc_global_wakeup_fd), &ev);
+  if (err < 0) {
+    gpr_log(GPR_ERROR, "epoll_ctl add for %d failed: %s", GRPC_WAKEUP_FD_GET_READ_FD(&grpc_global_wakeup_fd),
+            strerror(errno));
+  }
+
   for (i = 0; i < nfds; i++) {
     multipoll_with_epoll_pollset_add_fd(exec_ctx, pollset, fds[i], 0);
   }
diff --git a/src/core/iomgr/pollset_multipoller_with_poll_posix.c b/src/core/iomgr/pollset_multipoller_with_poll_posix.c
index 63e0b9e..1356ebe 100644
--- a/src/core/iomgr/pollset_multipoller_with_poll_posix.c
+++ b/src/core/iomgr/pollset_multipoller_with_poll_posix.c
@@ -114,13 +114,16 @@
   h = pollset->data.ptr;
   timeout = grpc_poll_deadline_to_millis_timeout(deadline, now);
   /* TODO(ctiller): perform just one malloc here if we exceed the inline case */
-  pfds = gpr_malloc(sizeof(*pfds) * (h->fd_count + 1));
-  watchers = gpr_malloc(sizeof(*watchers) * (h->fd_count + 1));
+  pfds = gpr_malloc(sizeof(*pfds) * (h->fd_count + 2));
+  watchers = gpr_malloc(sizeof(*watchers) * (h->fd_count + 2));
   fd_count = 0;
-  pfd_count = 1;
-  pfds[0].fd = GRPC_WAKEUP_FD_GET_READ_FD(&worker->wakeup_fd);
+  pfd_count = 2;
+  pfds[0].fd = GRPC_WAKEUP_FD_GET_READ_FD(&grpc_global_wakeup_fd);
   pfds[0].events = POLLIN;
-  pfds[0].revents = POLLOUT;
+  pfds[0].revents = 0;
+  pfds[1].fd = GRPC_WAKEUP_FD_GET_READ_FD(&worker->wakeup_fd);
+  pfds[1].events = POLLIN;
+  pfds[1].revents = 0;
   for (i = 0; i < h->fd_count; i++) {
     int remove = grpc_fd_is_orphaned(h->fds[i]);
     for (j = 0; !remove && j < h->del_count; j++) {
@@ -143,7 +146,7 @@
   h->fd_count = fd_count;
   gpr_mu_unlock(&pollset->mu);
 
-  for (i = 1; i < pfd_count; i++) {
+  for (i = 2; i < pfd_count; i++) {
     pfds[i].events = (short)grpc_fd_begin_poll(watchers[i].fd, pollset, POLLIN,
                                                POLLOUT, &watchers[i]);
   }
@@ -154,7 +157,7 @@
   r = grpc_poll_function(pfds, pfd_count, timeout);
   GRPC_SCHEDULING_END_BLOCKING_REGION;
 
-  for (i = 1; i < pfd_count; i++) {
+  for (i = 2; i < pfd_count; i++) {
     grpc_fd_end_poll(exec_ctx, &watchers[i], pfds[i].revents & POLLIN,
                      pfds[i].revents & POLLOUT);
   }
@@ -167,9 +170,12 @@
     /* do nothing */
   } else {
     if (pfds[0].revents & POLLIN) {
+      grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd);
+    }
+    if (pfds[1].revents & POLLIN) {
       grpc_wakeup_fd_consume_wakeup(&worker->wakeup_fd);
     }
-    for (i = 1; i < pfd_count; i++) {
+    for (i = 2; i < pfd_count; i++) {
       if (watchers[i].fd == NULL) {
         continue;
       }
diff --git a/src/core/iomgr/pollset_posix.c b/src/core/iomgr/pollset_posix.c
index f9d6aad..82a82cc 100644
--- a/src/core/iomgr/pollset_posix.c
+++ b/src/core/iomgr/pollset_posix.c
@@ -57,8 +57,16 @@
 GPR_TLS_DECL(g_current_thread_poller);
 GPR_TLS_DECL(g_current_thread_worker);
 
+/** Default poll() function - a pointer so that it can be overridden by some
+ *  tests */
 grpc_poll_function_type grpc_poll_function = poll;
 
+/** The alarm system needs to be able to wakeup 'some poller' sometimes
+ *  (specifically when a new alarm needs to be triggered earlier than the next
+ *  alarm 'epoch').
+ *  This wakeup_fd gives us something to alert on when such a case occurs. */
+grpc_wakeup_fd grpc_global_wakeup_fd;
+
 static void remove_worker(grpc_pollset *p, grpc_pollset_worker *worker) {
   worker->prev->next = worker->next;
   worker->next->prev = worker->prev;
@@ -121,12 +129,18 @@
   gpr_tls_init(&g_current_thread_poller);
   gpr_tls_init(&g_current_thread_worker);
   grpc_wakeup_fd_global_init();
+  grpc_wakeup_fd_init(&grpc_global_wakeup_fd);
 }
 
 void grpc_pollset_global_shutdown(void) {
+  grpc_wakeup_fd_destroy(&grpc_global_wakeup_fd);
+  grpc_wakeup_fd_global_destroy();
   gpr_tls_destroy(&g_current_thread_poller);
   gpr_tls_destroy(&g_current_thread_worker);
-  grpc_wakeup_fd_global_destroy();
+}
+
+void grpc_kick_poller(void) {
+  grpc_wakeup_fd_wakeup(&grpc_global_wakeup_fd);
 }
 
 /* main interface */
@@ -193,6 +207,8 @@
     goto done;
   }
   if (grpc_alarm_check(exec_ctx, now, &deadline)) {
+    gpr_mu_unlock(&pollset->mu);
+    locked = 0;
     goto done;
   }
   if (pollset->shutting_down) {
@@ -294,7 +310,7 @@
   }
   timeout = gpr_time_sub(deadline, now);
   return gpr_time_to_millis(gpr_time_add(
-      timeout, gpr_time_from_nanos(GPR_NS_PER_SEC - 1, GPR_TIMESPAN)));
+      timeout, gpr_time_from_nanos(GPR_NS_PER_MS - 1, GPR_TIMESPAN)));
 }
 
 /*
@@ -439,7 +455,7 @@
                                                 grpc_pollset_worker *worker,
                                                 gpr_timespec deadline,
                                                 gpr_timespec now) {
-  struct pollfd pfd[2];
+  struct pollfd pfd[3];
   grpc_fd *fd;
   grpc_fd_watcher fd_watcher;
   int timeout;
@@ -452,17 +468,20 @@
     fd = pollset->data.ptr = NULL;
   }
   timeout = grpc_poll_deadline_to_millis_timeout(deadline, now);
-  pfd[0].fd = GRPC_WAKEUP_FD_GET_READ_FD(&worker->wakeup_fd);
+  pfd[0].fd = GRPC_WAKEUP_FD_GET_READ_FD(&grpc_global_wakeup_fd);
   pfd[0].events = POLLIN;
   pfd[0].revents = 0;
-  nfds = 1;
+  pfd[1].fd = GRPC_WAKEUP_FD_GET_READ_FD(&worker->wakeup_fd);
+  pfd[1].events = POLLIN;
+  pfd[1].revents = 0;
+  nfds = 2;
   if (fd) {
-    pfd[1].fd = fd->fd;
-    pfd[1].revents = 0;
+    pfd[2].fd = fd->fd;
+    pfd[2].revents = 0;
     gpr_mu_unlock(&pollset->mu);
-    pfd[1].events =
+    pfd[2].events =
         (short)grpc_fd_begin_poll(fd, pollset, POLLIN, POLLOUT, &fd_watcher);
-    if (pfd[1].events != 0) {
+    if (pfd[2].events != 0) {
       nfds++;
     }
   } else {
@@ -479,8 +498,8 @@
   GRPC_TIMER_MARK(GRPC_PTAG_POLL_FINISHED, r);
 
   if (fd) {
-    grpc_fd_end_poll(exec_ctx, &fd_watcher, pfd[1].revents & POLLIN,
-                     pfd[1].revents & POLLOUT);
+    grpc_fd_end_poll(exec_ctx, &fd_watcher, pfd[2].revents & POLLIN,
+                     pfd[2].revents & POLLOUT);
   }
 
   if (r < 0) {
@@ -491,13 +510,16 @@
     /* do nothing */
   } else {
     if (pfd[0].revents & POLLIN) {
+      grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd);
+    }
+    if (pfd[1].revents & POLLIN) {
       grpc_wakeup_fd_consume_wakeup(&worker->wakeup_fd);
     }
-    if (nfds > 1) {
-      if (pfd[1].revents & (POLLIN | POLLHUP | POLLERR)) {
+    if (nfds > 2) {
+      if (pfd[2].revents & (POLLIN | POLLHUP | POLLERR)) {
         grpc_fd_become_readable(exec_ctx, fd);
       }
-      if (pfd[1].revents & (POLLOUT | POLLHUP | POLLERR)) {
+      if (pfd[2].revents & (POLLOUT | POLLHUP | POLLERR)) {
         grpc_fd_become_writable(exec_ctx, fd);
       }
     }
diff --git a/src/core/iomgr/pollset_posix.h b/src/core/iomgr/pollset_posix.h
index f996dd1..83c5258 100644
--- a/src/core/iomgr/pollset_posix.h
+++ b/src/core/iomgr/pollset_posix.h
@@ -129,5 +129,6 @@
 /* override to allow tests to hook poll() usage */
 typedef int (*grpc_poll_function_type)(struct pollfd *, nfds_t, int);
 extern grpc_poll_function_type grpc_poll_function;
+extern grpc_wakeup_fd grpc_global_wakeup_fd;
 
 #endif /* GRPC_INTERNAL_CORE_IOMGR_POLLSET_POSIX_H */
diff --git a/src/core/iomgr/pollset_windows.c b/src/core/iomgr/pollset_windows.c
index 798b637..1b4935a 100644
--- a/src/core/iomgr/pollset_windows.c
+++ b/src/core/iomgr/pollset_windows.c
@@ -230,4 +230,8 @@
   }
 }
 
+void grpc_kick_poller(void) {
+  grpc_iocp_kick();
+}
+
 #endif /* GPR_WINSOCK_SOCKET */
diff --git a/test/core/end2end/README b/test/core/end2end/README
index 59daec4..a18172a 100644
--- a/test/core/end2end/README
+++ b/test/core/end2end/README
@@ -3,5 +3,5 @@
 
 To add a new test or fixture:
 - add the code to the relevant directory
-- update gen_build_json.py to reflect the change
+- update gen_build_yaml.py to reflect the change
 - regenerate projects
diff --git a/test/core/end2end/tests/channel_connectivity.c b/test/core/end2end/tests/channel_connectivity.c
index 0b7a8a6..be9b7fd 100644
--- a/test/core/end2end/tests/channel_connectivity.c
+++ b/test/core/end2end/tests/channel_connectivity.c
@@ -34,19 +34,49 @@
 #include "test/core/end2end/end2end_tests.h"
 
 #include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd.h>
 #include <grpc/support/time.h>
 
 #include "test/core/end2end/cq_verifier.h"
 
 static void *tag(gpr_intptr t) { return (void *)t; }
 
+typedef struct {
+  gpr_event started;
+  grpc_channel *channel;
+  grpc_completion_queue *cq;
+} child_events;
+
+static void child_thread(void *arg) {
+  child_events *ce = arg;
+  grpc_event ev;
+  gpr_event_set(&ce->started, (void*)1);
+  gpr_log(GPR_DEBUG, "verifying");
+  ev = grpc_completion_queue_next(ce->cq, gpr_inf_future(GPR_CLOCK_MONOTONIC), NULL);
+  GPR_ASSERT(ev.type == GRPC_OP_COMPLETE);
+  GPR_ASSERT(ev.tag == tag(1));
+  GPR_ASSERT(ev.success == 0);
+}
+
 static void test_connectivity(grpc_end2end_test_config config) {
   grpc_end2end_test_fixture f = config.create_fixture(NULL, NULL);
   grpc_connectivity_state state;
   cq_verifier *cqv = cq_verifier_create(f.cq);
+  child_events ce;
+  gpr_thd_options thdopt = gpr_thd_options_default();
+  gpr_thd_id thdid;
 
   config.init_client(&f, NULL);
 
+  ce.channel = f.client;
+  ce.cq = f.cq;
+  gpr_event_init(&ce.started);
+  gpr_thd_options_set_joinable(&thdopt);
+  GPR_ASSERT(gpr_thd_new(&thdid, child_thread, &ce, &thdopt));
+
+  gpr_event_wait(&ce.started, gpr_inf_future(GPR_CLOCK_MONOTONIC));
+
   /* channels should start life in IDLE, and stay there */
   GPR_ASSERT(grpc_channel_check_connectivity_state(f.client, 0) ==
              GRPC_CHANNEL_IDLE);
@@ -55,18 +85,24 @@
              GRPC_CHANNEL_IDLE);
 
   /* start watching for a change */
+  gpr_log(GPR_DEBUG, "watching");
   grpc_channel_watch_connectivity_state(f.client, GRPC_CHANNEL_IDLE,
-                                        GRPC_TIMEOUT_SECONDS_TO_DEADLINE(3),
+                                        gpr_now(GPR_CLOCK_MONOTONIC),
                                         f.cq, tag(1));
-  /* nothing should happen */
-  cq_verify_empty(cqv);
+
+  /* eventually the child thread completion should trigger */
+  gpr_thd_join(thdid);
 
   /* check that we're still in idle, and start connecting */
   GPR_ASSERT(grpc_channel_check_connectivity_state(f.client, 1) ==
              GRPC_CHANNEL_IDLE);
+  /* start watching for a change */
+  grpc_channel_watch_connectivity_state(f.client, GRPC_CHANNEL_IDLE,
+                                        GRPC_TIMEOUT_SECONDS_TO_DEADLINE(3),
+                                        f.cq, tag(2));
 
   /* and now the watch should trigger */
-  cq_expect_completion(cqv, tag(1), 1);
+  cq_expect_completion(cqv, tag(2), 1);
   cq_verify(cqv);
   state = grpc_channel_check_connectivity_state(f.client, 0);
   GPR_ASSERT(state == GRPC_CHANNEL_TRANSIENT_FAILURE ||
@@ -75,8 +111,8 @@
   /* quickly followed by a transition to TRANSIENT_FAILURE */
   grpc_channel_watch_connectivity_state(f.client, GRPC_CHANNEL_CONNECTING,
                                         GRPC_TIMEOUT_SECONDS_TO_DEADLINE(3),
-                                        f.cq, tag(2));
-  cq_expect_completion(cqv, tag(2), 1);
+                                        f.cq, tag(3));
+  cq_expect_completion(cqv, tag(3), 1);
   cq_verify(cqv);
   state = grpc_channel_check_connectivity_state(f.client, 0);
   GPR_ASSERT(state == GRPC_CHANNEL_TRANSIENT_FAILURE ||
@@ -93,8 +129,8 @@
      READY is reached */
   while (state != GRPC_CHANNEL_READY) {
     grpc_channel_watch_connectivity_state(
-        f.client, state, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(3), f.cq, tag(3));
-    cq_expect_completion(cqv, tag(3), 1);
+        f.client, state, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(3), f.cq, tag(4));
+    cq_expect_completion(cqv, tag(4), 1);
     cq_verify(cqv);
     state = grpc_channel_check_connectivity_state(f.client, 0);
     GPR_ASSERT(state == GRPC_CHANNEL_READY ||
@@ -108,11 +144,11 @@
 
   grpc_channel_watch_connectivity_state(f.client, GRPC_CHANNEL_READY,
                                         GRPC_TIMEOUT_SECONDS_TO_DEADLINE(3),
-                                        f.cq, tag(4));
+                                        f.cq, tag(5));
 
   grpc_server_shutdown_and_notify(f.server, f.cq, tag(0xdead));
 
-  cq_expect_completion(cqv, tag(4), 1);
+  cq_expect_completion(cqv, tag(5), 1);
   cq_expect_completion(cqv, tag(0xdead), 1);
   cq_verify(cqv);
   state = grpc_channel_check_connectivity_state(f.client, 0);
diff --git a/test/core/util/test_config.c b/test/core/util/test_config.c
index 30caf4b..84b376a 100644
--- a/test/core/util/test_config.c
+++ b/test/core/util/test_config.c
@@ -35,6 +35,7 @@
 
 #include <grpc/support/port_platform.h>
 #include <grpc/support/log.h>
+#include "src/core/support/string.h"
 #include <stdlib.h>
 #include <signal.h>
 
@@ -88,25 +89,43 @@
 #include <stdio.h>
 #include <string.h>
 #include <grpc/support/useful.h>
+#include <errno.h>
 
 static char g_alt_stack[MINSIGSTKSZ];
 
 #define MAX_FRAMES 32
 
+/* signal safe output */
+static void output_string(const char *string) {
+  size_t len = strlen(string);
+  ssize_t r;
+
+  do {
+    r = write(STDERR_FILENO, string, len);
+  } while (r == -1 && errno == EINTR);
+}
+
+static void output_num(long num) {
+  char buf[GPR_LTOA_MIN_BUFSIZE];
+  gpr_ltoa(num, buf);
+  output_string(buf);
+}
+
 static void crash_handler(int signum, siginfo_t *info, void *data) {
   void *addrlist[MAX_FRAMES + 1];
   int addrlen;
-  int i;
-  char **symlist;
 
-  fprintf(stderr, "Caught signal %d\n", signum);
+  output_string("\n\n\n*******************************\nCaught signal ");
+  output_num(signum);
+  output_string("\n");
+
   addrlen = backtrace(addrlist, GPR_ARRAY_SIZE(addrlist));
 
-  symlist = backtrace_symbols(addrlist, addrlen);
-  for (i = 0; i < addrlen; i++) {
-    fprintf(stderr, "  %s\n", symlist[i]);
+  if (addrlen == 0) {
+    output_string("  no backtrace\n");
+  } else {
+    backtrace_symbols_fd(addrlist, addrlen, STDERR_FILENO);
   }
-  free(symlist);
 
   raise(signum);
 }
@@ -114,6 +133,7 @@
 static void install_crash_handler() {
   stack_t ss;
   struct sigaction sa;
+
   memset(&ss, 0, sizeof(ss));
   memset(&sa, 0, sizeof(sa));
   ss.ss_size = sizeof(g_alt_stack);
diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc
index 2659b0e..49a38ce 100644
--- a/test/cpp/end2end/end2end_test.cc
+++ b/test/cpp/end2end/end2end_test.cc
@@ -62,7 +62,6 @@
 
 namespace grpc {
 namespace testing {
-
 namespace {
 
 const char* kServerCancelAfterReads = "cancel_after_reads";
@@ -195,8 +194,6 @@
 const char TestAuthMetadataProcessor::kIdentityPropName[] = "novel identity";
 
 
-}  // namespace
-
 class Proxy : public ::grpc::cpp::test::util::TestService::Service {
  public:
   Proxy(std::shared_ptr<Channel> channel)
@@ -353,14 +350,24 @@
   }
 };
 
-/* Param is whether or not to use a proxy -- some tests use TEST_F as they don't
-   need this functionality */
-class End2endTest : public ::testing::TestWithParam<bool> {
+class TestScenario {
+ public:
+  TestScenario(bool proxy, bool tls) : use_proxy(proxy), use_tls(tls) {}
+  void Log() const {
+    gpr_log(GPR_INFO, "Scenario: proxy %d, tls %d", use_proxy, use_tls);
+  }
+  bool use_proxy;
+  bool use_tls;
+};
+
+class End2endTest : public ::testing::TestWithParam<TestScenario> {
  protected:
   End2endTest()
       : is_server_started_(false),
         kMaxMessageSize_(8192),
-        special_service_("special") {}
+        special_service_("special") {
+    GetParam().Log();
+  }
 
   void TearDown() GRPC_OVERRIDE {
     if (is_server_started_) {
@@ -374,13 +381,16 @@
     server_address_ << "127.0.0.1:" << port;
     // Setup server
     ServerBuilder builder;
-    SslServerCredentialsOptions::PemKeyCertPair pkcp = {test_server1_key,
-                                                        test_server1_cert};
-    SslServerCredentialsOptions ssl_opts;
-    ssl_opts.pem_root_certs = "";
-    ssl_opts.pem_key_cert_pairs.push_back(pkcp);
-    auto server_creds = SslServerCredentials(ssl_opts);
-    server_creds->SetAuthMetadataProcessor(processor);
+    auto server_creds = InsecureServerCredentials();
+    if (GetParam().use_tls) {
+      SslServerCredentialsOptions::PemKeyCertPair pkcp = {test_server1_key,
+                                                          test_server1_cert};
+      SslServerCredentialsOptions ssl_opts;
+      ssl_opts.pem_root_certs = "";
+      ssl_opts.pem_key_cert_pairs.push_back(pkcp);
+      server_creds = SslServerCredentials(ssl_opts);
+      server_creds->SetAuthMetadataProcessor(processor);
+    }
     builder.AddListeningPort(server_address_.str(), server_creds);
     builder.RegisterService(&service_);
     builder.RegisterService("foo.test.youtube.com", &special_service_);
@@ -396,17 +406,20 @@
       StartServer(std::shared_ptr<AuthMetadataProcessor>());
     }
     EXPECT_TRUE(is_server_started_);
-    SslCredentialsOptions ssl_opts = {test_root_cert, "", ""};
     ChannelArguments args;
-    args.SetSslTargetNameOverride("foo.test.google.fr");
+    auto channel_creds = InsecureCredentials();
+    if (GetParam().use_tls) {
+      SslCredentialsOptions ssl_opts = {test_root_cert, "", ""};
+      args.SetSslTargetNameOverride("foo.test.google.fr");
+      channel_creds = SslCredentials(ssl_opts);
+    }
     args.SetString(GRPC_ARG_SECONDARY_USER_AGENT_STRING, "end2end_test");
-    channel_ = CreateCustomChannel(server_address_.str(),
-                                   SslCredentials(ssl_opts), args);
+    channel_ = CreateCustomChannel(server_address_.str(), channel_creds, args);
   }
 
-  void ResetStub(bool use_proxy) {
+  void ResetStub() {
     ResetChannel();
-    if (use_proxy) {
+    if (GetParam().use_proxy) {
       proxy_service_.reset(new Proxy(channel_));
       int port = grpc_pick_unused_port_or_die();
       std::ostringstream proxyaddr;
@@ -450,124 +463,8 @@
   }
 }
 
-TEST_F(End2endTest, SimpleRpcWithHost) {
-  ResetStub(false);
-
-  EchoRequest request;
-  EchoResponse response;
-  request.set_message("Hello");
-
-  ClientContext context;
-  context.set_authority("foo.test.youtube.com");
-  Status s = stub_->Echo(&context, request, &response);
-  EXPECT_EQ(response.message(), request.message());
-  EXPECT_TRUE(response.has_param());
-  EXPECT_EQ("special", response.param().host());
-  EXPECT_TRUE(s.ok());
-}
-
-TEST_P(End2endTest, SimpleRpc) {
-  ResetStub(GetParam());
-  SendRpc(stub_.get(), 1);
-}
-
-TEST_P(End2endTest, MultipleRpcs) {
-  ResetStub(GetParam());
-  std::vector<std::thread*> threads;
-  for (int i = 0; i < 10; ++i) {
-    threads.push_back(new std::thread(SendRpc, stub_.get(), 10));
-  }
-  for (int i = 0; i < 10; ++i) {
-    threads[i]->join();
-    delete threads[i];
-  }
-}
-
-// Set a 10us deadline and make sure proper error is returned.
-TEST_P(End2endTest, RpcDeadlineExpires) {
-  ResetStub(GetParam());
-  EchoRequest request;
-  EchoResponse response;
-  request.set_message("Hello");
-
-  ClientContext context;
-  std::chrono::system_clock::time_point deadline =
-      std::chrono::system_clock::now() + std::chrono::microseconds(10);
-  context.set_deadline(deadline);
-  Status s = stub_->Echo(&context, request, &response);
-  EXPECT_EQ(StatusCode::DEADLINE_EXCEEDED, s.error_code());
-}
-
-// Set a long but finite deadline.
-TEST_P(End2endTest, RpcLongDeadline) {
-  ResetStub(GetParam());
-  EchoRequest request;
-  EchoResponse response;
-  request.set_message("Hello");
-
-  ClientContext context;
-  std::chrono::system_clock::time_point deadline =
-      std::chrono::system_clock::now() + std::chrono::hours(1);
-  context.set_deadline(deadline);
-  Status s = stub_->Echo(&context, request, &response);
-  EXPECT_EQ(response.message(), request.message());
-  EXPECT_TRUE(s.ok());
-}
-
-// Ask server to echo back the deadline it sees.
-TEST_P(End2endTest, EchoDeadline) {
-  ResetStub(GetParam());
-  EchoRequest request;
-  EchoResponse response;
-  request.set_message("Hello");
-  request.mutable_param()->set_echo_deadline(true);
-
-  ClientContext context;
-  std::chrono::system_clock::time_point deadline =
-      std::chrono::system_clock::now() + std::chrono::seconds(100);
-  context.set_deadline(deadline);
-  Status s = stub_->Echo(&context, request, &response);
-  EXPECT_EQ(response.message(), request.message());
-  EXPECT_TRUE(s.ok());
-  gpr_timespec sent_deadline;
-  Timepoint2Timespec(deadline, &sent_deadline);
-  // Allow 1 second error.
-  EXPECT_LE(response.param().request_deadline() - sent_deadline.tv_sec, 1);
-  EXPECT_GE(response.param().request_deadline() - sent_deadline.tv_sec, -1);
-}
-
-// Ask server to echo back the deadline it sees. The rpc has no deadline.
-TEST_P(End2endTest, EchoDeadlineForNoDeadlineRpc) {
-  ResetStub(GetParam());
-  EchoRequest request;
-  EchoResponse response;
-  request.set_message("Hello");
-  request.mutable_param()->set_echo_deadline(true);
-
-  ClientContext context;
-  Status s = stub_->Echo(&context, request, &response);
-  EXPECT_EQ(response.message(), request.message());
-  EXPECT_TRUE(s.ok());
-  EXPECT_EQ(response.param().request_deadline(),
-            gpr_inf_future(GPR_CLOCK_REALTIME).tv_sec);
-}
-
-TEST_P(End2endTest, UnimplementedRpc) {
-  ResetStub(GetParam());
-  EchoRequest request;
-  EchoResponse response;
-  request.set_message("Hello");
-
-  ClientContext context;
-  Status s = stub_->Unimplemented(&context, request, &response);
-  EXPECT_FALSE(s.ok());
-  EXPECT_EQ(s.error_code(), grpc::StatusCode::UNIMPLEMENTED);
-  EXPECT_EQ(s.error_message(), "");
-  EXPECT_EQ(response.message(), "");
-}
-
-TEST_F(End2endTest, RequestStreamOneRequest) {
-  ResetStub(false);
+TEST_P(End2endTest, RequestStreamOneRequest) {
+  ResetStub();
   EchoRequest request;
   EchoResponse response;
   ClientContext context;
@@ -581,8 +478,8 @@
   EXPECT_TRUE(s.ok());
 }
 
-TEST_F(End2endTest, RequestStreamTwoRequests) {
-  ResetStub(false);
+TEST_P(End2endTest, RequestStreamTwoRequests) {
+  ResetStub();
   EchoRequest request;
   EchoResponse response;
   ClientContext context;
@@ -597,8 +494,8 @@
   EXPECT_TRUE(s.ok());
 }
 
-TEST_F(End2endTest, ResponseStream) {
-  ResetStub(false);
+TEST_P(End2endTest, ResponseStream) {
+  ResetStub();
   EchoRequest request;
   EchoResponse response;
   ClientContext context;
@@ -617,8 +514,8 @@
   EXPECT_TRUE(s.ok());
 }
 
-TEST_F(End2endTest, BidiStream) {
-  ResetStub(false);
+TEST_P(End2endTest, BidiStream) {
+  ResetStub();
   EchoRequest request;
   EchoResponse response;
   ClientContext context;
@@ -650,8 +547,8 @@
 
 // Talk to the two services with the same name but different package names.
 // The two stubs are created on the same channel.
-TEST_F(End2endTest, DiffPackageServices) {
-  ResetStub(false);
+TEST_P(End2endTest, DiffPackageServices) {
+  ResetStub();
   EchoRequest request;
   EchoResponse response;
   request.set_message("Hello");
@@ -670,33 +567,6 @@
   EXPECT_TRUE(s.ok());
 }
 
-// rpc and stream should fail on bad credentials.
-TEST_F(End2endTest, BadCredentials) {
-  std::shared_ptr<Credentials> bad_creds = GoogleRefreshTokenCredentials("");
-  EXPECT_EQ(static_cast<Credentials*>(nullptr), bad_creds.get());
-  std::shared_ptr<Channel> channel =
-      CreateChannel(server_address_.str(), bad_creds);
-  std::unique_ptr<grpc::cpp::test::util::TestService::Stub> stub(
-      grpc::cpp::test::util::TestService::NewStub(channel));
-  EchoRequest request;
-  EchoResponse response;
-  ClientContext context;
-  request.set_message("Hello");
-
-  Status s = stub->Echo(&context, request, &response);
-  EXPECT_EQ("", response.message());
-  EXPECT_FALSE(s.ok());
-  EXPECT_EQ(StatusCode::INVALID_ARGUMENT, s.error_code());
-  EXPECT_EQ("Invalid credentials.", s.error_message());
-
-  ClientContext context2;
-  auto stream = stub->BidiStream(&context2);
-  s = stream->Finish();
-  EXPECT_FALSE(s.ok());
-  EXPECT_EQ(StatusCode::INVALID_ARGUMENT, s.error_code());
-  EXPECT_EQ("Invalid credentials.", s.error_message());
-}
-
 void CancelRpc(ClientContext* context, int delay_us, TestServiceImpl* service) {
   gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
                                gpr_time_from_micros(delay_us, GPR_TIMESPAN)));
@@ -705,40 +575,9 @@
   context->TryCancel();
 }
 
-// Client cancels rpc after 10ms
-TEST_P(End2endTest, ClientCancelsRpc) {
-  ResetStub(GetParam());
-  EchoRequest request;
-  EchoResponse response;
-  request.set_message("Hello");
-  const int kCancelDelayUs = 10 * 1000;
-  request.mutable_param()->set_client_cancel_after_us(kCancelDelayUs);
-
-  ClientContext context;
-  std::thread cancel_thread(CancelRpc, &context, kCancelDelayUs, &service_);
-  Status s = stub_->Echo(&context, request, &response);
-  cancel_thread.join();
-  EXPECT_EQ(StatusCode::CANCELLED, s.error_code());
-  EXPECT_EQ(s.error_message(), "Cancelled");
-}
-
-// Server cancels rpc after 1ms
-TEST_P(End2endTest, ServerCancelsRpc) {
-  ResetStub(GetParam());
-  EchoRequest request;
-  EchoResponse response;
-  request.set_message("Hello");
-  request.mutable_param()->set_server_cancel_after_us(1000);
-
-  ClientContext context;
-  Status s = stub_->Echo(&context, request, &response);
-  EXPECT_EQ(StatusCode::CANCELLED, s.error_code());
-  EXPECT_TRUE(s.error_message().empty());
-}
-
 // Client cancels request stream after sending two messages
-TEST_F(End2endTest, ClientCancelsRequestStream) {
-  ResetStub(false);
+TEST_P(End2endTest, ClientCancelsRequestStream) {
+  ResetStub();
   EchoRequest request;
   EchoResponse response;
   ClientContext context;
@@ -757,8 +596,8 @@
 }
 
 // Client cancels server stream after sending some messages
-TEST_F(End2endTest, ClientCancelsResponseStream) {
-  ResetStub(false);
+TEST_P(End2endTest, ClientCancelsResponseStream) {
+  ResetStub();
   EchoRequest request;
   EchoResponse response;
   ClientContext context;
@@ -789,8 +628,8 @@
 }
 
 // Client cancels bidi stream after sending some messages
-TEST_F(End2endTest, ClientCancelsBidi) {
-  ResetStub(false);
+TEST_P(End2endTest, ClientCancelsBidi) {
+  ResetStub();
   EchoRequest request;
   EchoResponse response;
   ClientContext context;
@@ -821,8 +660,8 @@
   EXPECT_EQ(grpc::StatusCode::CANCELLED, s.error_code());
 }
 
-TEST_F(End2endTest, RpcMaxMessageSize) {
-  ResetStub(false);
+TEST_P(End2endTest, RpcMaxMessageSize) {
+  ResetStub();
   EchoRequest request;
   EchoResponse response;
   request.set_message(string(kMaxMessageSize_ * 2, 'a'));
@@ -832,6 +671,328 @@
   EXPECT_FALSE(s.ok());
 }
 
+// Client sends 20 requests and the server returns CANCELLED status after
+// reading 10 requests.
+TEST_P(End2endTest, RequestStreamServerEarlyCancelTest) {
+  ResetStub();
+  EchoRequest request;
+  EchoResponse response;
+  ClientContext context;
+
+  context.AddMetadata(kServerCancelAfterReads, "10");
+  auto stream = stub_->RequestStream(&context, &response);
+  request.set_message("hello");
+  int send_messages = 20;
+  while (send_messages > 0) {
+    EXPECT_TRUE(stream->Write(request));
+    send_messages--;
+  }
+  stream->WritesDone();
+  Status s = stream->Finish();
+  EXPECT_EQ(s.error_code(), StatusCode::CANCELLED);
+}
+
+void ReaderThreadFunc(ClientReaderWriter<EchoRequest, EchoResponse>* stream,
+                      gpr_event* ev) {
+  EchoResponse resp;
+  gpr_event_set(ev, (void*)1);
+  while (stream->Read(&resp)) {
+    gpr_log(GPR_INFO, "Read message");
+  }
+}
+
+// Run a Read and a WritesDone simultaneously.
+TEST_P(End2endTest, SimultaneousReadWritesDone) {
+  ResetStub();
+  ClientContext context;
+  gpr_event ev;
+  gpr_event_init(&ev);
+  auto stream = stub_->BidiStream(&context);
+  std::thread reader_thread(ReaderThreadFunc, stream.get(), &ev);
+  gpr_event_wait(&ev, gpr_inf_future(GPR_CLOCK_REALTIME));
+  stream->WritesDone();
+  Status s = stream->Finish();
+  EXPECT_TRUE(s.ok());
+  reader_thread.join();
+}
+
+TEST_P(End2endTest, ChannelState) {
+  ResetStub();
+  // Start IDLE
+  EXPECT_EQ(GRPC_CHANNEL_IDLE, channel_->GetState(false));
+
+  // Did not ask to connect, no state change.
+  CompletionQueue cq;
+  std::chrono::system_clock::time_point deadline =
+      std::chrono::system_clock::now() + std::chrono::milliseconds(10);
+  channel_->NotifyOnStateChange(GRPC_CHANNEL_IDLE, deadline, &cq, NULL);
+  void* tag;
+  bool ok = true;
+  cq.Next(&tag, &ok);
+  EXPECT_FALSE(ok);
+
+  EXPECT_EQ(GRPC_CHANNEL_IDLE, channel_->GetState(true));
+  EXPECT_TRUE(channel_->WaitForStateChange(GRPC_CHANNEL_IDLE,
+                                           gpr_inf_future(GPR_CLOCK_REALTIME)));
+  EXPECT_EQ(GRPC_CHANNEL_CONNECTING, channel_->GetState(false));
+}
+
+// Takes 10s.
+TEST_P(End2endTest, ChannelStateTimeout) {
+  if (GetParam().use_tls) {
+    return;
+  }
+  int port = grpc_pick_unused_port_or_die();
+  std::ostringstream server_address;
+  server_address << "127.0.0.1:" << port;
+  // Channel to non-existing server
+  auto channel = CreateChannel(server_address.str(), InsecureCredentials());
+  // Start IDLE
+  EXPECT_EQ(GRPC_CHANNEL_IDLE, channel->GetState(true));
+
+  auto state = GRPC_CHANNEL_IDLE;
+  for (int i = 0; i < 10; i++) {
+    channel->WaitForStateChange(
+        state, std::chrono::system_clock::now() + std::chrono::seconds(1));
+    state = channel->GetState(false);
+  }
+}
+
+// Talking to a non-existing service.
+TEST_P(End2endTest, NonExistingService) {
+  ResetChannel();
+  std::unique_ptr<grpc::cpp::test::util::UnimplementedService::Stub> stub;
+  stub = grpc::cpp::test::util::UnimplementedService::NewStub(channel_);
+
+  EchoRequest request;
+  EchoResponse response;
+  request.set_message("Hello");
+
+  ClientContext context;
+  Status s = stub->Unimplemented(&context, request, &response);
+  EXPECT_EQ(StatusCode::UNIMPLEMENTED, s.error_code());
+  EXPECT_EQ("", s.error_message());
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Test with and without a proxy.
+class ProxyEnd2endTest : public End2endTest {
+ protected:
+};
+
+TEST_P(ProxyEnd2endTest, SimpleRpc) {
+  ResetStub();
+  SendRpc(stub_.get(), 1);
+}
+
+TEST_P(ProxyEnd2endTest, MultipleRpcs) {
+  ResetStub();
+  std::vector<std::thread*> threads;
+  for (int i = 0; i < 10; ++i) {
+    threads.push_back(new std::thread(SendRpc, stub_.get(), 10));
+  }
+  for (int i = 0; i < 10; ++i) {
+    threads[i]->join();
+    delete threads[i];
+  }
+}
+
+// Set a 10us deadline and make sure proper error is returned.
+TEST_P(ProxyEnd2endTest, RpcDeadlineExpires) {
+  ResetStub();
+  EchoRequest request;
+  EchoResponse response;
+  request.set_message("Hello");
+
+  ClientContext context;
+  std::chrono::system_clock::time_point deadline =
+      std::chrono::system_clock::now() + std::chrono::microseconds(10);
+  context.set_deadline(deadline);
+  Status s = stub_->Echo(&context, request, &response);
+  EXPECT_EQ(StatusCode::DEADLINE_EXCEEDED, s.error_code());
+}
+
+// Set a long but finite deadline.
+TEST_P(ProxyEnd2endTest, RpcLongDeadline) {
+  ResetStub();
+  EchoRequest request;
+  EchoResponse response;
+  request.set_message("Hello");
+
+  ClientContext context;
+  std::chrono::system_clock::time_point deadline =
+      std::chrono::system_clock::now() + std::chrono::hours(1);
+  context.set_deadline(deadline);
+  Status s = stub_->Echo(&context, request, &response);
+  EXPECT_EQ(response.message(), request.message());
+  EXPECT_TRUE(s.ok());
+}
+
+// Ask server to echo back the deadline it sees.
+TEST_P(ProxyEnd2endTest, EchoDeadline) {
+  ResetStub();
+  EchoRequest request;
+  EchoResponse response;
+  request.set_message("Hello");
+  request.mutable_param()->set_echo_deadline(true);
+
+  ClientContext context;
+  std::chrono::system_clock::time_point deadline =
+      std::chrono::system_clock::now() + std::chrono::seconds(100);
+  context.set_deadline(deadline);
+  Status s = stub_->Echo(&context, request, &response);
+  EXPECT_EQ(response.message(), request.message());
+  EXPECT_TRUE(s.ok());
+  gpr_timespec sent_deadline;
+  Timepoint2Timespec(deadline, &sent_deadline);
+  // Allow 1 second error.
+  EXPECT_LE(response.param().request_deadline() - sent_deadline.tv_sec, 1);
+  EXPECT_GE(response.param().request_deadline() - sent_deadline.tv_sec, -1);
+}
+
+// Ask server to echo back the deadline it sees. The rpc has no deadline.
+TEST_P(ProxyEnd2endTest, EchoDeadlineForNoDeadlineRpc) {
+  ResetStub();
+  EchoRequest request;
+  EchoResponse response;
+  request.set_message("Hello");
+  request.mutable_param()->set_echo_deadline(true);
+
+  ClientContext context;
+  Status s = stub_->Echo(&context, request, &response);
+  EXPECT_EQ(response.message(), request.message());
+  EXPECT_TRUE(s.ok());
+  EXPECT_EQ(response.param().request_deadline(),
+            gpr_inf_future(GPR_CLOCK_REALTIME).tv_sec);
+}
+
+TEST_P(ProxyEnd2endTest, UnimplementedRpc) {
+  ResetStub();
+  EchoRequest request;
+  EchoResponse response;
+  request.set_message("Hello");
+
+  ClientContext context;
+  Status s = stub_->Unimplemented(&context, request, &response);
+  EXPECT_FALSE(s.ok());
+  EXPECT_EQ(s.error_code(), grpc::StatusCode::UNIMPLEMENTED);
+  EXPECT_EQ(s.error_message(), "");
+  EXPECT_EQ(response.message(), "");
+}
+
+// Client cancels rpc after 10ms
+TEST_P(ProxyEnd2endTest, ClientCancelsRpc) {
+  ResetStub();
+  EchoRequest request;
+  EchoResponse response;
+  request.set_message("Hello");
+  const int kCancelDelayUs = 10 * 1000;
+  request.mutable_param()->set_client_cancel_after_us(kCancelDelayUs);
+
+  ClientContext context;
+  std::thread cancel_thread(CancelRpc, &context, kCancelDelayUs, &service_);
+  Status s = stub_->Echo(&context, request, &response);
+  cancel_thread.join();
+  EXPECT_EQ(StatusCode::CANCELLED, s.error_code());
+  EXPECT_EQ(s.error_message(), "Cancelled");
+}
+
+// Server cancels rpc after 1ms
+TEST_P(ProxyEnd2endTest, ServerCancelsRpc) {
+  ResetStub();
+  EchoRequest request;
+  EchoResponse response;
+  request.set_message("Hello");
+  request.mutable_param()->set_server_cancel_after_us(1000);
+
+  ClientContext context;
+  Status s = stub_->Echo(&context, request, &response);
+  EXPECT_EQ(StatusCode::CANCELLED, s.error_code());
+  EXPECT_TRUE(s.error_message().empty());
+}
+
+// Make the response larger than the flow control window.
+TEST_P(ProxyEnd2endTest, HugeResponse) {
+  ResetStub();
+  EchoRequest request;
+  EchoResponse response;
+  request.set_message("huge response");
+  const size_t kResponseSize = 1024 * (1024 + 10);
+  request.mutable_param()->set_response_message_length(kResponseSize);
+
+  ClientContext context;
+  Status s = stub_->Echo(&context, request, &response);
+  EXPECT_EQ(kResponseSize, response.message().size());
+  EXPECT_TRUE(s.ok());
+}
+
+TEST_P(ProxyEnd2endTest, Peer) {
+  ResetStub();
+  EchoRequest request;
+  EchoResponse response;
+  request.set_message("hello");
+  request.mutable_param()->set_echo_peer(true);
+
+  ClientContext context;
+  Status s = stub_->Echo(&context, request, &response);
+  EXPECT_EQ(response.message(), request.message());
+  EXPECT_TRUE(s.ok());
+  EXPECT_TRUE(CheckIsLocalhost(response.param().peer()));
+  EXPECT_TRUE(CheckIsLocalhost(context.peer()));
+}
+
+//////////////////////////////////////////////////////////////////////////
+class SecureEnd2endTest : public End2endTest {
+ protected:
+  SecureEnd2endTest() {
+    GPR_ASSERT(!GetParam().use_proxy);
+    GPR_ASSERT(GetParam().use_tls);
+  }
+};
+
+TEST_P(SecureEnd2endTest, SimpleRpcWithHost) {
+  ResetStub();
+
+  EchoRequest request;
+  EchoResponse response;
+  request.set_message("Hello");
+
+  ClientContext context;
+  context.set_authority("foo.test.youtube.com");
+  Status s = stub_->Echo(&context, request, &response);
+  EXPECT_EQ(response.message(), request.message());
+  EXPECT_TRUE(response.has_param());
+  EXPECT_EQ("special", response.param().host());
+  EXPECT_TRUE(s.ok());
+}
+
+// rpc and stream should fail on bad credentials.
+TEST_P(SecureEnd2endTest, BadCredentials) {
+  std::shared_ptr<Credentials> bad_creds = GoogleRefreshTokenCredentials("");
+  EXPECT_EQ(static_cast<Credentials*>(nullptr), bad_creds.get());
+  std::shared_ptr<Channel> channel =
+      CreateChannel(server_address_.str(), bad_creds);
+  std::unique_ptr<grpc::cpp::test::util::TestService::Stub> stub(
+      grpc::cpp::test::util::TestService::NewStub(channel));
+  EchoRequest request;
+  EchoResponse response;
+  ClientContext context;
+  request.set_message("Hello");
+
+  Status s = stub->Echo(&context, request, &response);
+  EXPECT_EQ("", response.message());
+  EXPECT_FALSE(s.ok());
+  EXPECT_EQ(StatusCode::INVALID_ARGUMENT, s.error_code());
+  EXPECT_EQ("Invalid credentials.", s.error_message());
+
+  ClientContext context2;
+  auto stream = stub->BidiStream(&context2);
+  s = stream->Finish();
+  EXPECT_FALSE(s.ok());
+  EXPECT_EQ(StatusCode::INVALID_ARGUMENT, s.error_code());
+  EXPECT_EQ("Invalid credentials.", s.error_message());
+}
+
 bool MetadataContains(
     const std::multimap<grpc::string_ref, grpc::string_ref>& metadata,
     const grpc::string& key, const grpc::string& value) {
@@ -847,8 +1008,45 @@
   return count == 1;
 }
 
-TEST_F(End2endTest, SetPerCallCredentials) {
-  ResetStub(false);
+TEST_P(SecureEnd2endTest, BlockingAuthMetadataPluginAndProcessorSuccess) {
+  auto* processor = new TestAuthMetadataProcessor(true);
+  StartServer(std::shared_ptr<AuthMetadataProcessor>(processor));
+  ResetStub();
+  EchoRequest request;
+  EchoResponse response;
+  ClientContext context;
+  context.set_credentials(processor->GetCompatibleClientCreds());
+  request.set_message("Hello");
+  request.mutable_param()->set_echo_metadata(true);
+  request.mutable_param()->set_expected_client_identity(
+      TestAuthMetadataProcessor::kGoodGuy);
+
+  Status s = stub_->Echo(&context, request, &response);
+  EXPECT_EQ(request.message(), response.message());
+  EXPECT_TRUE(s.ok());
+
+  // Metadata should have been consumed by the processor.
+  EXPECT_FALSE(MetadataContains(
+      context.GetServerTrailingMetadata(), GRPC_AUTHORIZATION_METADATA_KEY,
+      grpc::string("Bearer ") + TestAuthMetadataProcessor::kGoodGuy));
+}
+
+TEST_P(SecureEnd2endTest, BlockingAuthMetadataPluginAndProcessorFailure) {
+  auto* processor = new TestAuthMetadataProcessor(true);
+  StartServer(std::shared_ptr<AuthMetadataProcessor>(processor));
+  ResetStub();
+  EchoRequest request;
+  EchoResponse response;
+  ClientContext context;
+  context.set_credentials(processor->GetIncompatibleClientCreds());
+  request.set_message("Hello");
+
+  Status s = stub_->Echo(&context, request, &response);
+  EXPECT_FALSE(s.ok());
+  EXPECT_EQ(s.error_code(), StatusCode::UNAUTHENTICATED);
+}
+TEST_P(SecureEnd2endTest, SetPerCallCredentials) {
+  ResetStub();
   EchoRequest request;
   EchoResponse response;
   ClientContext context;
@@ -869,8 +1067,8 @@
                                "fake_selector"));
 }
 
-TEST_F(End2endTest, InsecurePerCallCredentials) {
-  ResetStub(false);
+TEST_P(SecureEnd2endTest, InsecurePerCallCredentials) {
+  ResetStub();
   EchoRequest request;
   EchoResponse response;
   ClientContext context;
@@ -884,8 +1082,8 @@
   EXPECT_EQ("Failed to set credentials to rpc.", s.error_message());
 }
 
-TEST_F(End2endTest, OverridePerCallCredentials) {
-  ResetStub(false);
+TEST_P(SecureEnd2endTest, OverridePerCallCredentials) {
+  ResetStub();
   EchoRequest request;
   EchoResponse response;
   ClientContext context;
@@ -915,8 +1113,8 @@
   EXPECT_TRUE(s.ok());
 }
 
-TEST_F(End2endTest, NonBlockingAuthMetadataPluginFailure) {
-  ResetStub(false);
+TEST_P(SecureEnd2endTest, NonBlockingAuthMetadataPluginFailure) {
+  ResetStub();
   EchoRequest request;
   EchoResponse response;
   ClientContext context;
@@ -932,10 +1130,10 @@
   EXPECT_EQ(s.error_code(), StatusCode::UNAUTHENTICATED);
 }
 
-TEST_F(End2endTest, NonBlockingAuthMetadataPluginAndProcessorSuccess) {
+TEST_P(SecureEnd2endTest, NonBlockingAuthMetadataPluginAndProcessorSuccess) {
   auto* processor = new TestAuthMetadataProcessor(false);
   StartServer(std::shared_ptr<AuthMetadataProcessor>(processor));
-  ResetStub(false);
+  ResetStub();
   EchoRequest request;
   EchoResponse response;
   ClientContext context;
@@ -955,10 +1153,10 @@
       grpc::string("Bearer ") + TestAuthMetadataProcessor::kGoodGuy));
 }
 
-TEST_F(End2endTest, NonBlockingAuthMetadataPluginAndProcessorFailure) {
+TEST_P(SecureEnd2endTest, NonBlockingAuthMetadataPluginAndProcessorFailure) {
   auto* processor = new TestAuthMetadataProcessor(false);
   StartServer(std::shared_ptr<AuthMetadataProcessor>(processor));
-  ResetStub(false);
+  ResetStub();
   EchoRequest request;
   EchoResponse response;
   ClientContext context;
@@ -970,8 +1168,8 @@
   EXPECT_EQ(s.error_code(), StatusCode::UNAUTHENTICATED);
 }
 
-TEST_F(End2endTest, BlockingAuthMetadataPluginFailure) {
-  ResetStub(false);
+TEST_P(SecureEnd2endTest, BlockingAuthMetadataPluginFailure) {
+  ResetStub();
   EchoRequest request;
   EchoResponse response;
   ClientContext context;
@@ -987,67 +1185,8 @@
   EXPECT_EQ(s.error_code(), StatusCode::UNAUTHENTICATED);
 }
 
-TEST_F(End2endTest, BlockingAuthMetadataPluginAndProcessorSuccess) {
-  auto* processor = new TestAuthMetadataProcessor(true);
-  StartServer(std::shared_ptr<AuthMetadataProcessor>(processor));
-  ResetStub(false);
-  EchoRequest request;
-  EchoResponse response;
-  ClientContext context;
-  context.set_credentials(processor->GetCompatibleClientCreds());
-  request.set_message("Hello");
-  request.mutable_param()->set_echo_metadata(true);
-  request.mutable_param()->set_expected_client_identity(
-      TestAuthMetadataProcessor::kGoodGuy);
-
-  Status s = stub_->Echo(&context, request, &response);
-  EXPECT_EQ(request.message(), response.message());
-  EXPECT_TRUE(s.ok());
-
-  // Metadata should have been consumed by the processor.
-  EXPECT_FALSE(MetadataContains(
-      context.GetServerTrailingMetadata(), GRPC_AUTHORIZATION_METADATA_KEY,
-      grpc::string("Bearer ") + TestAuthMetadataProcessor::kGoodGuy));
-}
-
-TEST_F(End2endTest, BlockingAuthMetadataPluginAndProcessorFailure) {
-  auto* processor = new TestAuthMetadataProcessor(true);
-  StartServer(std::shared_ptr<AuthMetadataProcessor>(processor));
-  ResetStub(false);
-  EchoRequest request;
-  EchoResponse response;
-  ClientContext context;
-  context.set_credentials(processor->GetIncompatibleClientCreds());
-  request.set_message("Hello");
-
-  Status s = stub_->Echo(&context, request, &response);
-  EXPECT_FALSE(s.ok());
-  EXPECT_EQ(s.error_code(), StatusCode::UNAUTHENTICATED);
-}
-
-// Client sends 20 requests and the server returns CANCELLED status after
-// reading 10 requests.
-TEST_F(End2endTest, RequestStreamServerEarlyCancelTest) {
-  ResetStub(false);
-  EchoRequest request;
-  EchoResponse response;
-  ClientContext context;
-
-  context.AddMetadata(kServerCancelAfterReads, "10");
-  auto stream = stub_->RequestStream(&context, &response);
-  request.set_message("hello");
-  int send_messages = 20;
-  while (send_messages > 0) {
-    EXPECT_TRUE(stream->Write(request));
-    send_messages--;
-  }
-  stream->WritesDone();
-  Status s = stream->Finish();
-  EXPECT_EQ(s.error_code(), StatusCode::CANCELLED);
-}
-
-TEST_F(End2endTest, ClientAuthContext) {
-  ResetStub(false);
+TEST_P(SecureEnd2endTest, ClientAuthContext) {
+  ResetStub();
   EchoRequest request;
   EchoResponse response;
   request.set_message("Hello");
@@ -1072,119 +1211,20 @@
   EXPECT_EQ("*.test.youtube.com", ToString(auth_ctx->GetPeerIdentity()[2]));
 }
 
-// Make the response larger than the flow control window.
-TEST_P(End2endTest, HugeResponse) {
-  ResetStub(GetParam());
-  EchoRequest request;
-  EchoResponse response;
-  request.set_message("huge response");
-  const size_t kResponseSize = 1024 * (1024 + 10);
-  request.mutable_param()->set_response_message_length(kResponseSize);
+INSTANTIATE_TEST_CASE_P(End2end, End2endTest,
+                        ::testing::Values(TestScenario(false, true),
+                                          TestScenario(false, false)));
 
-  ClientContext context;
-  Status s = stub_->Echo(&context, request, &response);
-  EXPECT_EQ(kResponseSize, response.message().size());
-  EXPECT_TRUE(s.ok());
-}
+INSTANTIATE_TEST_CASE_P(ProxyEnd2end, ProxyEnd2endTest,
+                        ::testing::Values(TestScenario(true, true),
+                                          TestScenario(true, false),
+                                          TestScenario(false, true),
+                                          TestScenario(false, false)));
 
-namespace {
-void ReaderThreadFunc(ClientReaderWriter<EchoRequest, EchoResponse>* stream,
-                      gpr_event* ev) {
-  EchoResponse resp;
-  gpr_event_set(ev, (void*)1);
-  while (stream->Read(&resp)) {
-    gpr_log(GPR_INFO, "Read message");
-  }
-}
+INSTANTIATE_TEST_CASE_P(SecureEnd2end, SecureEnd2endTest,
+                        ::testing::Values(TestScenario(false, true)));
+
 }  // namespace
-
-// Run a Read and a WritesDone simultaneously.
-TEST_F(End2endTest, SimultaneousReadWritesDone) {
-  ResetStub(false);
-  ClientContext context;
-  gpr_event ev;
-  gpr_event_init(&ev);
-  auto stream = stub_->BidiStream(&context);
-  std::thread reader_thread(ReaderThreadFunc, stream.get(), &ev);
-  gpr_event_wait(&ev, gpr_inf_future(GPR_CLOCK_REALTIME));
-  stream->WritesDone();
-  Status s = stream->Finish();
-  EXPECT_TRUE(s.ok());
-  reader_thread.join();
-}
-
-TEST_P(End2endTest, Peer) {
-  ResetStub(GetParam());
-  EchoRequest request;
-  EchoResponse response;
-  request.set_message("hello");
-  request.mutable_param()->set_echo_peer(true);
-
-  ClientContext context;
-  Status s = stub_->Echo(&context, request, &response);
-  EXPECT_EQ(response.message(), request.message());
-  EXPECT_TRUE(s.ok());
-  EXPECT_TRUE(CheckIsLocalhost(response.param().peer()));
-  EXPECT_TRUE(CheckIsLocalhost(context.peer()));
-}
-
-TEST_F(End2endTest, ChannelState) {
-  ResetStub(false);
-  // Start IDLE
-  EXPECT_EQ(GRPC_CHANNEL_IDLE, channel_->GetState(false));
-
-  // Did not ask to connect, no state change.
-  CompletionQueue cq;
-  std::chrono::system_clock::time_point deadline =
-      std::chrono::system_clock::now() + std::chrono::milliseconds(10);
-  channel_->NotifyOnStateChange(GRPC_CHANNEL_IDLE, deadline, &cq, NULL);
-  void* tag;
-  bool ok = true;
-  cq.Next(&tag, &ok);
-  EXPECT_FALSE(ok);
-
-  EXPECT_EQ(GRPC_CHANNEL_IDLE, channel_->GetState(true));
-  EXPECT_TRUE(channel_->WaitForStateChange(GRPC_CHANNEL_IDLE,
-                                           gpr_inf_future(GPR_CLOCK_REALTIME)));
-  EXPECT_EQ(GRPC_CHANNEL_CONNECTING, channel_->GetState(false));
-}
-
-// Takes 10s.
-TEST_F(End2endTest, ChannelStateTimeout) {
-  int port = grpc_pick_unused_port_or_die();
-  std::ostringstream server_address;
-  server_address << "127.0.0.1:" << port;
-  // Channel to non-existing server
-  auto channel = CreateChannel(server_address.str(), InsecureCredentials());
-  // Start IDLE
-  EXPECT_EQ(GRPC_CHANNEL_IDLE, channel->GetState(true));
-
-  auto state = GRPC_CHANNEL_IDLE;
-  for (int i = 0; i < 10; i++) {
-    channel->WaitForStateChange(
-        state, std::chrono::system_clock::now() + std::chrono::seconds(1));
-    state = channel->GetState(false);
-  }
-}
-
-// Talking to a non-existing service.
-TEST_F(End2endTest, NonExistingService) {
-  ResetChannel();
-  std::unique_ptr<grpc::cpp::test::util::UnimplementedService::Stub> stub;
-  stub = grpc::cpp::test::util::UnimplementedService::NewStub(channel_);
-
-  EchoRequest request;
-  EchoResponse response;
-  request.set_message("Hello");
-
-  ClientContext context;
-  Status s = stub->Unimplemented(&context, request, &response);
-  EXPECT_EQ(StatusCode::UNIMPLEMENTED, s.error_code());
-  EXPECT_EQ("", s.error_message());
-}
-
-INSTANTIATE_TEST_CASE_P(End2end, End2endTest, ::testing::Values(false, true));
-
 }  // namespace testing
 }  // namespace grpc
 
diff --git a/vsprojects/coapp/zlib/README.md b/vsprojects/coapp/zlib/README.md
new file mode 100644
index 0000000..c81a57a
--- /dev/null
+++ b/vsprojects/coapp/zlib/README.md
@@ -0,0 +1,35 @@
+Zlib Native Nuget package
+-------------------------
+
+Uses [CoApp](http://coapp.org/) project to build the zlib package.
+
+Prerequisites
+-------------
+Multiple versions of VS installed to be able to build all the targets:
+* Visual Studio 2013
+* Visual Studio 2010 (you might need SP1 to prevent LNK1123 error)
+
+CoApp toolkit: http://downloads.coapp.org/files/CoApp.Tools.Powershell.msi
+
+More details on installation: http://coapp.org/tutorials/installation.html
+
+Building
+--------
+
+Build all flavors of zlib library using the provided batch file.
+```
+buildall.bat
+```
+
+Then, create NuGet package using powershell (you'll need the CoApp toolkit installed):
+```
+[THIS_DIRECTORY]> Write-NuGetPackage grpc.dependencies.zlib.autopkg
+```
+
+This will create three NuGet packages:
+* the main dev package
+* the redistributable package that contains just the binaries and no headers
+* the symbols package (debug symbols)
+
+Later, you can push the package to NuGet.org repo.
+Attention: before pusing the resulting nuget package to public nuget repo, you have to be 100% sure it works correctly - there’s no way how to delete or update an already existing package.
\ No newline at end of file
diff --git a/vsprojects/coapp/zlib/buildall.bat b/vsprojects/coapp/zlib/buildall.bat
new file mode 100644
index 0000000..a71e56f
--- /dev/null
+++ b/vsprojects/coapp/zlib/buildall.bat
@@ -0,0 +1,51 @@
+@echo off
+setlocal
+
+setlocal
+call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" amd64
+call :build x64 Release v120 || goto :eof
+call :build x64 Debug v120 || goto :eof
+endlocal
+
+setlocal
+call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86
+call :build Win32 Release v120 || goto :eof
+call :build Win32 Debug v120 || goto :eof
+endlocal
+
+REM setlocal
+REM call "C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat" amd64
+REM call :build x64 Release v110 || goto :eof
+REM call :build x64 Debug v110 || goto :eof
+REM endlocal
+
+REM setlocal
+REM call "C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat" x86
+REM call :build Win32 Release v110 || goto :eof
+REM call :build Win32 Debug v110 || goto :eof
+REM endlocal
+
+REM setlocal
+REM call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" amd64
+REM call :build x64 Release v100 || goto :eof
+REM call :build x64 Debug v100 || goto :eof
+REM endlocal
+
+setlocal
+call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x86
+call :build Win32 Release v100 || goto :eof
+call :build Win32 Debug v100 || goto :eof
+endlocal
+
+goto :eof
+
+:build
+msbuild /P:Platform=%1 /P:Configuration=%2 /P:PlatformToolset=%3 /P:UsesConfigurationType=Dynamic /P:CallingConvention=cdecl .\zlib.sln || goto :eof
+msbuild /P:Platform=%1 /P:Configuration=%2 /P:PlatformToolset=%3 /P:UsesConfigurationType=Dynamic /P:CallingConvention=stdcall .\zlib.sln || goto :eof
+msbuild /P:Platform=%1 /P:Configuration=%2 /P:PlatformToolset=%3 /P:UsesConfigurationType=Static /P:CallingConvention=cdecl .\zlib.sln || goto :eof
+msbuild /P:Platform=%1 /P:Configuration=%2 /P:PlatformToolset=%3 /P:UsesConfigurationType=Static /P:CallingConvention=stdcall .\zlib.sln || goto :eof
+msbuild /P:Platform=%1 /P:Configuration=%2 /P:PlatformToolset=%3 /P:UsesConfigurationType=ltcg /P:CallingConvention=cdecl .\zlib.sln || goto :eof
+msbuild /P:Platform=%1 /P:Configuration=%2 /P:PlatformToolset=%3 /P:UsesConfigurationType=ltcg /P:CallingConvention=stdcall .\zlib.sln || goto :eof
+goto :eof
+
+
diff --git a/vsprojects/coapp/zlib/grpc.dependencies.zlib.autopkg b/vsprojects/coapp/zlib/grpc.dependencies.zlib.autopkg
new file mode 100644
index 0000000..01390b9
--- /dev/null
+++ b/vsprojects/coapp/zlib/grpc.dependencies.zlib.autopkg
@@ -0,0 +1,102 @@
+@import @"version.inc";
+
+configurations {
+};
+
+#define { 
+      package-id = "grpc.dependencies.zlib";
+}
+
+nuget {
+	// the nuspec file metadata. Gets created/updated on build
+	nuspec {
+		id = ${package-id};
+		version : ${package-version};
+		title: gRPC Native Dependency: ZLib compression library;
+		authors: {Jean-loup Gailly, Mark Adler, Garrett Serack, Tim Rogers};
+		owners: {Jan Tattermusch};
+		licenseUrl: "http://zlib.net/zlib-license.html";
+		projectUrl: "http://github.com/jtattermusch/zlib";
+		iconUrl: "http://zlib.net/images/zlib3d-b1.png";
+		requireLicenseAcceptance:false;
+		summary:A zlib library;
+		description: @"A native zlib library.
+	zlib homepage:  http://zlib.net";
+		releaseNotes: "Release of zlib 1.2.8 libraries.";
+		copyright: Copyright 2013;
+		tags: { zlib, native, CoApp };
+
+	};
+
+	// the files that go into the content folders
+	// (inserted into the nuspec file)
+	files {
+	    // .targets file that are applied when redist package is installed from a managed project.
+		managed_build: {
+		  #output {
+              package = redist;
+          };
+		  #destination = "\build\portable-net45";
+		  "managed_targets\${package-id}.redist.props";
+		  "managed_targets\${package-id}.redist.targets";
+		};
+	
+		include: { ..\..\..\third_party\zlib\zlib.h, ..\..\..\third_party\zlib\zconf.h };
+
+		docs: {  ..\..\..\third_party\zlib\doc\**\* };
+        
+        source += {
+            "..\..\..\third_party\zlib\adler32.c",
+            "..\..\..\third_party\zlib\compress.c",
+            "..\..\..\third_party\zlib\crc32.c",
+            "..\..\..\third_party\zlib\deflate.c",
+            "..\..\..\third_party\zlib\gzclose.c",
+            "..\..\..\third_party\zlib\gzlib.c",
+            "..\..\..\third_party\zlib\gzread.c",
+            "..\..\..\third_party\zlib\gzwrite.c",
+            "..\..\..\third_party\zlib\infback.c",
+            "..\..\..\third_party\zlib\inffast.c",
+            "..\..\..\third_party\zlib\inflate.c",
+            "..\..\..\third_party\zlib\inftrees.c",
+            "..\..\..\third_party\zlib\trees.c",
+            "..\..\..\third_party\zlib\uncompr.c",
+            "..\..\..\third_party\zlib\zutil.c",
+            "..\..\..\third_party\zlib\crc32.h",
+            "..\..\..\third_party\zlib\deflate.h",
+            "..\..\..\third_party\zlib\gzguts.h",
+            "..\..\..\third_party\zlib\inffast.h",
+            "..\..\..\third_party\zlib\inffixed.h",
+            "..\..\..\third_party\zlib\inflate.h",
+            "..\..\..\third_party\zlib\inftrees.h",
+            "..\..\..\third_party\zlib\trees.h",
+            "..\..\..\third_party\zlib\zconf.h",
+            "..\..\..\third_party\zlib\zlib.h",
+            "..\..\..\third_party\zlib\zutil.h",
+            "..\..\..\third_party\zlib\contrib\masmx64\inffas8664.c",
+        };
+		("v100,v120", "Win32,x64", "Release,Debug", "Dynamic", "cdecl,stdcall", "MultiByte") => {
+			[${0},${1},${2},${3},${4}] {
+				lib:     { .\output\${0}\${1}\${2}\${3}\${4}\${5}\*.lib };
+				bin:     { .\output\${0}\${1}\${2}\${3}\${4}\${5}\*.dll };
+				symbols: { .\output\${0}\${1}\${2}\${3}\${4}\${5}\*.pdb };
+			};
+		};
+		("v100,v120", "Win32,x64", "Release,Debug", "Static,ltcg", "cdecl,stdcall", "MultiByte") => {
+			[${0},${1},${2},${3},${4}] {
+				lib:     { .\output\${0}\${1}\${2}\${3}\${4}\${5}\*.lib };
+			};
+		};
+
+	};
+
+	// the VC++ .targets file that gets generated and inserted into the ${d_content} folder
+	targets {
+		Defines += HAS_ZLIB;
+		[dynamic]
+			Defines += ZLIB_DLL;
+		[stdcall]
+			Defines += ZLIB_WINAPI;
+			
+	};
+}
+
diff --git a/vsprojects/coapp/zlib/managed_targets/grpc.dependencies.zlib.redist.props b/vsprojects/coapp/zlib/managed_targets/grpc.dependencies.zlib.redist.props
new file mode 100644
index 0000000..bcb37de
--- /dev/null
+++ b/vsprojects/coapp/zlib/managed_targets/grpc.dependencies.zlib.redist.props
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <!-- Whether or not copy native dependencies to output directory after building -->
+    <CopyNativeDependencies Condition=" '$(CopyNativeDependencies)' == '' ">true</CopyNativeDependencies>
+    
+	<!-- Set defaults for native dependencies if not already set. Properties can be overriden in the project files. -->
+	<NativeDependenciesToolset Condition=" '$(NativeDependenciesToolset)' == '' ">v120</NativeDependenciesToolset>
+    <NativeDependenciesPlatform Condition=" '$(NativeDependenciesPlatform)' == '' ">Win32</NativeDependenciesPlatform>
+    <NativeDependenciesConfiguration Condition=" '$(NativeDependenciesConfiguration)' == '' ">Debug</NativeDependenciesConfiguration>
+	<ZlibCallingConvention Condition=" '$(ZlibCallingConvention)' == '' ">cdecl</ZlibCallingConvention>
+  </PropertyGroup>
+</Project>
\ No newline at end of file
diff --git a/vsprojects/coapp/zlib/managed_targets/grpc.dependencies.zlib.redist.targets b/vsprojects/coapp/zlib/managed_targets/grpc.dependencies.zlib.redist.targets
new file mode 100644
index 0000000..f00d97d
--- /dev/null
+++ b/vsprojects/coapp/zlib/managed_targets/grpc.dependencies.zlib.redist.targets
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Choose>
+    <!-- Under older versions of Monodevelop, Choose is not supported and is just ignored, which gives us the desired effect. -->
+    <When Condition=" '$(OS)' != 'Unix' ">
+      <ItemGroup Condition=" '$(CopyNativeDependencies)' == 'true' ">
+        <Content Include="$(MSBuildThisFileDirectory)..\..\build\native\bin\$(NativeDependenciesToolset)\$(NativeDependenciesPlatform)\$(NativeDependenciesConfiguration)\dynamic\$(ZlibCallingConvention)\zlib.dll">
+          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+        </Content>
+      </ItemGroup>
+	</When>
+  <Otherwise />
+  </Choose>
+</Project>
\ No newline at end of file
diff --git a/vsprojects/coapp/zlib/version.inc b/vsprojects/coapp/zlib/version.inc
new file mode 100644
index 0000000..1f893a4
--- /dev/null
+++ b/vsprojects/coapp/zlib/version.inc
@@ -0,0 +1 @@
+#define { package-version : 1.2.8.9; } 
diff --git a/vsprojects/coapp/zlib/zlib.sln b/vsprojects/coapp/zlib/zlib.sln
new file mode 100644
index 0000000..7c38e6e
--- /dev/null
+++ b/vsprojects/coapp/zlib/zlib.sln
@@ -0,0 +1,26 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "zlib.vcxproj", "{63BED288-E8C3-4345-B84D-2E64598DCF3A}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+		Debug|x64 = Debug|x64
+		Release|x64 = Release|x64
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{63BED288-E8C3-4345-B84D-2E64598DCF3A}.Debug|Win32.ActiveCfg = Debug|Win32
+		{63BED288-E8C3-4345-B84D-2E64598DCF3A}.Debug|Win32.Build.0 = Debug|Win32
+		{63BED288-E8C3-4345-B84D-2E64598DCF3A}.Release|Win32.ActiveCfg = Release|Win32
+		{63BED288-E8C3-4345-B84D-2E64598DCF3A}.Release|Win32.Build.0 = Release|Win32
+		{63BED288-E8C3-4345-B84D-2E64598DCF3A}.Debug|x64.ActiveCfg = Debug|x64
+		{63BED288-E8C3-4345-B84D-2E64598DCF3A}.Debug|x64.Build.0 = Debug|x64
+		{63BED288-E8C3-4345-B84D-2E64598DCF3A}.Release|x64.ActiveCfg = Release|x64
+		{63BED288-E8C3-4345-B84D-2E64598DCF3A}.Release|x64.Build.0 = Release|x64
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
diff --git a/vsprojects/coapp/zlib/zlib.vcxproj b/vsprojects/coapp/zlib/zlib.vcxproj
new file mode 100644
index 0000000..56df39a
--- /dev/null
+++ b/vsprojects/coapp/zlib/zlib.vcxproj
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="PreBuildCmds;PreBuildTargets;Build;PostBuildCmds;PostBuildTargets" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup Label="CoApp" >
+    <CoAppEtcDirectory>$(registry:HKEY_LOCAL_MACHINE\Software\Outercurve\CoApp.Powershell\etc)</CoAppEtcDirectory>
+    
+    <!-- Set to true to make visual studio use PTK when building.-->
+    <UsePTKFromVisualStudio>false</UsePTKFromVisualStudio>
+  </PropertyGroup>
+
+  <PropertyGroup Label="Configuration" >
+    <!-- This lets Visual Studio see this as a VC12 project by default -->
+    <PlatformToolset Condition="'$(PlatformToolset)' == ''">v120</PlatformToolset>
+  </PropertyGroup>
+
+  <Import Condition="Exists('$(CoAppEtcDirectory)\common-variables.vcxproj')" Project="$(CoAppEtcDirectory)\common-variables.vcxproj" />
+  
+  <PropertyGroup Label="CustomSettings" />
+  
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{63BED288-E8C3-4345-B84D-2E64598DCF3A}</ProjectGuid>
+    <RootNamespace>$(MSBuildProjectName)</RootNamespace>
+    
+    <OutNameSuffix Condition="$(IS_CDECL) AND $(IS_DYNAMIC)">1</OutNameSuffix>
+
+    <!-- set to Application or DynamicLibrary (DynamicLibrary can get altered to StaticLibrary by PTK -->
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+
+    <!-- Common Compiler Defines (semicolon delimited) -->
+    <Defines>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;</Defines>
+    <Defines Condition="$(IS_STDCALL)">$(Defines);ZLIB_WINAPI;</Defines>
+    <Defines Condition="$(IS_DYNAMIC)">$(Defines);ZLIB_DLL;</Defines>
+    <Defines Condition="'$(AppContainer)' == 'App'">$(Defines);IOWIN32_USING_WINRT_API=1;</Defines>
+    <Defines Condition="'$(AppContainer)' != 'App'">$(Defines);IOWIN32_USING_WINRT_API=0;</Defines>
+    <Defines Condition="'$(UseASM)' == 'ASM'">$(Defines);ASMV;ASMINF;</Defines>
+    <Defines Condition="$(IS_X64)">$(Defines);WIN64;</Defines>
+
+    <!-- Additional Include folders (semicolon delimited) -->
+    <IncludeDirectories>..\..\..\third_party\zlib;</IncludeDirectories>
+
+    <!-- Additional Library folders (semicolon delimited) -->
+    <LibraryDirectories></LibraryDirectories>
+
+    <!-- Libraries to Link with -->
+    <Libraries></Libraries>
+    
+    <!-- Batch script to run before Build-->
+    <PreBuild></PreBuild>
+
+    <!-- Batch script to run after Build-->
+    <PostBuild></PostBuild>
+
+    <!-- Batch script to run before Link step-->
+    <PreLink></PreLink>
+
+    <!-- Batch script to run after Link-->
+    <PostLink></PostLink>
+
+    <!-- Batch script to run before Lib step-->
+    <PreLib></PreLib>
+
+    <!-- Batch script to run after Lib-->
+    <PostLib></PostLib>
+
+    <!-- Targets to run before Build (semcolon delimited)-->
+    <PreBuildTargets></PreBuildTargets>
+
+    <!-- Targets to run before Build (semcolon delimited)-->
+    <PostBuildTargets></PostBuildTargets>
+  
+    <!-- for Dynamic libs, you can specify the Module .DEF file path -->
+    <ModuleDefinitionFile></ModuleDefinitionFile>
+  </PropertyGroup>
+
+  <Import Condition="Exists('$(CoAppEtcDirectory)\common-header.vcxproj')" Project="$(CoAppEtcDirectory)\common-header.vcxproj" />
+  <Import Condition="'$(UseASM)' == 'ASM'" Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
+
+  <PropertyGroup>
+    <OutDir>$(ProjectRootDir)Output/$(PlatformToolset)/$(Platform)/$(Configuration)/$(UsesConfigurationType)/$(CallingConvention)/$(CharacterSet)/$(AppContainer)/$(UseASM)/</OutDir>
+    <IntDir>$(ProjectRootDir)Intermediate/$(TargetName)/$(PlatformToolset)/$(Platform)/$(Configuration)/$(UsesConfigurationType)/$(CallingConvention)/$(CharacterSet)/$(AppContainer)/$(UseASM)/</IntDir>
+  </PropertyGroup>
+
+  <ItemDefinitionGroup>
+    <ClCompile>
+	  <SDLCheck>true</SDLCheck>
+    </ClCompile>
+    <Link>
+      <BaseAddress>0x5A4C0000</BaseAddress>
+    </Link>
+  </ItemDefinitionGroup>
+  
+  <ItemGroup Condition="'$(UseASM)' == 'ASM'">
+    <MASM Include="..\..\..\third_party\zlib\contrib\masmx64\gvmat64.asm" Condition="$(IS_X64)">
+      <AssembledCodeListingFile>$(IntDir)gvmat64.lst</AssembledCodeListingFile>
+      <MASMBeforeTargets>Build</MASMBeforeTargets>
+      <ObjectFileName>$(IntDir)gvmat64.obj</ObjectFileName>
+    </MASM>
+    <MASM Include="..\..\..\third_party\zlib\contrib\masmx64\inffasx64.asm" Condition="$(IS_X64)">
+      <AssembledCodeListingFile>$(IntDir)inffasx64.lst</AssembledCodeListingFile>
+      <MASMBeforeTargets>Build</MASMBeforeTargets>
+      <ObjectFileName>$(IntDir)inffasx64.obj</ObjectFileName>
+    </MASM>
+    <MASM Include="..\..\..\third_party\zlib\contrib\masmx86\inffas32.asm" Condition="$(IS_X86) AND $(IS_STDCALL)">
+      <AssembledCodeListingFile>$(IntDir)inffas32.lst</AssembledCodeListingFile>
+      <MASMBeforeTargets>Build</MASMBeforeTargets>
+	  <UseSafeExceptionHandlers>true</UseSafeExceptionHandlers>
+      <AdditionalOptions>/coff %(AdditionalOptions)</AdditionalOptions>
+      <ObjectFileName>$(IntDir)inffas32.obj</ObjectFileName>
+    </MASM>
+    <MASM Include="..\..\..\third_party\zlib\contrib\masmx86\match686.asm" Condition="$(IS_X86) AND $(IS_STDCALL)">
+      <AssembledCodeListingFile>$(IntDir)match686.lst</AssembledCodeListingFile>
+      <MASMBeforeTargets>Build</MASMBeforeTargets>
+	  <UseSafeExceptionHandlers>true</UseSafeExceptionHandlers>
+      <AdditionalOptions>/coff %(AdditionalOptions)</AdditionalOptions>
+      <ObjectFileName>$(IntDir)match686.obj</ObjectFileName>
+    </MASM>
+  </ItemGroup>
+
+  <ItemGroup Label="C Source Files">
+    <!-- Include the source files to compile here -->
+    <!-- <ClCompile Include="..\src\foo.c" /> -->
+    <ClCompile Include="..\..\..\third_party\zlib\adler32.c" />
+    <ClCompile Include="..\..\..\third_party\zlib\compress.c" />
+    <ClCompile Include="..\..\..\third_party\zlib\crc32.c" />
+    <ClCompile Include="..\..\..\third_party\zlib\deflate.c" />
+    <ClCompile Include="..\..\..\third_party\zlib\gzclose.c" />
+    <ClCompile Include="..\..\..\third_party\zlib\gzlib.c" />
+    <ClCompile Include="..\..\..\third_party\zlib\gzread.c" />
+    <ClCompile Include="..\..\..\third_party\zlib\gzwrite.c" />
+    <ClCompile Include="..\..\..\third_party\zlib\infback.c" />
+    <ClCompile Include="..\..\..\third_party\zlib\contrib\masmx64\inffas8664.c" Condition="$(IS_X64)" />
+    <ClCompile Include="..\..\..\third_party\zlib\inffast.c" />
+    <ClCompile Include="..\..\..\third_party\zlib\inflate.c" />
+    <ClCompile Include="..\..\..\third_party\zlib\inftrees.c" />
+    <ClCompile Include="..\..\..\third_party\zlib\trees.c" />
+    <ClCompile Include="..\..\..\third_party\zlib\uncompr.c" />
+    <ClCompile Include="..\..\..\third_party\zlib\zutil.c" />
+  </ItemGroup>
+
+  <ItemGroup Label="Resource Files">
+    <!-- Include the source files to compile here -->
+    <!-- <ResourceCompile Include="..\src\foo.rc" /> -->
+    <ResourceCompile Include="..\..\..\third_party\zlib\win32\zlib1.rc" />
+  </ItemGroup>
+
+  <Import Condition="'$(UseASM)' == 'ASM'" Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
+  <Import Condition="Exists('$(CoAppEtcDirectory)\common-footer.vcxproj')" Project="$(CoAppEtcDirectory)\common-footer.vcxproj" />
+</Project>