From 10006758d0b3f53a3a996413b550120bfe4a4feb Mon Sep 17 00:00:00 2001
From: Jan Tattermusch <jtattermusch@google.com>
Date: Thu, 23 Jul 2020 15:16:32 +0200
Subject: [PATCH 01/38] Fix repeated builds broken by re2's cmake

---
 cmake/re2.cmake | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/cmake/re2.cmake b/cmake/re2.cmake
index 383d64faa72..3e83aae6910 100644
--- a/cmake/re2.cmake
+++ b/cmake/re2.cmake
@@ -21,6 +21,9 @@ if(gRPC_RE2_PROVIDER STREQUAL "module")
     set(RE2_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/re2)
   endif()
   if(EXISTS "${RE2_ROOT_DIR}/CMakeLists.txt")
+    # Explicitly disable BUILD_TESTING to avoid re2's CMakeLists.txt triggering https://github.com/grpc/grpc/issues/23586
+    option(BUILD_TESTING "re2.cmake explicitly disabled CTest's BUILD_TESTING option." OFF)
+
     include_directories("${RE2_ROOT_DIR}")
     add_subdirectory(${RE2_ROOT_DIR} third_party/re2)
 

From 2fc4cfadcf878b403f44bb81007d2c8eb0114548 Mon Sep 17 00:00:00 2001
From: Karthik Ravi Shankar <karthikrs@google.com>
Date: Thu, 23 Jul 2020 16:18:23 -0700
Subject: [PATCH 02/38] Move ServerCredentials from ::grpc_impl to ::grpc

Reverts: https://github.com/grpc/grpc/pull/18452
---
 BUILD                                         |  1 -
 BUILD.gn                                      |  1 -
 CMakeLists.txt                                |  2 -
 Makefile                                      |  2 -
 build_autogenerated.yaml                      |  2 -
 gRPC-C++.podspec                              |  1 -
 .../grpcpp/impl/codegen/server_interface.h    |  4 +-
 include/grpcpp/security/server_credentials.h  | 98 +++++++++----------
 .../grpcpp/security/server_credentials_impl.h | 89 -----------------
 include/grpcpp/server_builder.h               |  8 +-
 .../external_connection_acceptor_impl.h       |  1 -
 src/cpp/server/insecure_server_credentials.cc |  4 +-
 src/cpp/server/secure_server_credentials.cc   |  6 +-
 src/cpp/server/secure_server_credentials.h    | 13 +--
 src/cpp/server/server_credentials.cc          |  6 +-
 tools/doxygen/Doxyfile.c++                    |  1 -
 tools/doxygen/Doxyfile.c++.internal           |  1 -
 17 files changed, 62 insertions(+), 178 deletions(-)
 delete mode 100644 include/grpcpp/security/server_credentials_impl.h

diff --git a/BUILD b/BUILD
index 036c782ac30..0635f9e21b5 100644
--- a/BUILD
+++ b/BUILD
@@ -253,7 +253,6 @@ GRPCXX_PUBLIC_HDRS = [
     "include/grpcpp/security/credentials.h",
     "include/grpcpp/security/credentials_impl.h",
     "include/grpcpp/security/server_credentials.h",
-    "include/grpcpp/security/server_credentials_impl.h",
     "include/grpcpp/security/tls_credentials_options.h",
     "include/grpcpp/server.h",
     "include/grpcpp/server_impl.h",
diff --git a/BUILD.gn b/BUILD.gn
index 59835609c50..e645069d933 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1179,7 +1179,6 @@ config("grpc_config") {
         "include/grpcpp/security/credentials.h",
         "include/grpcpp/security/credentials_impl.h",
         "include/grpcpp/security/server_credentials.h",
-        "include/grpcpp/security/server_credentials_impl.h",
         "include/grpcpp/security/tls_credentials_options.h",
         "include/grpcpp/server.h",
         "include/grpcpp/server_builder.h",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1b85e61f25a..342237b4b3f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2821,7 +2821,6 @@ foreach(_hdr
   include/grpcpp/security/credentials.h
   include/grpcpp/security/credentials_impl.h
   include/grpcpp/security/server_credentials.h
-  include/grpcpp/security/server_credentials_impl.h
   include/grpcpp/security/tls_credentials_options.h
   include/grpcpp/server.h
   include/grpcpp/server_builder.h
@@ -3515,7 +3514,6 @@ foreach(_hdr
   include/grpcpp/security/credentials.h
   include/grpcpp/security/credentials_impl.h
   include/grpcpp/security/server_credentials.h
-  include/grpcpp/security/server_credentials_impl.h
   include/grpcpp/security/tls_credentials_options.h
   include/grpcpp/server.h
   include/grpcpp/server_builder.h
diff --git a/Makefile b/Makefile
index 40e2642512c..42625b211cc 100644
--- a/Makefile
+++ b/Makefile
@@ -4971,7 +4971,6 @@ PUBLIC_HEADERS_CXX += \
     include/grpcpp/security/credentials.h \
     include/grpcpp/security/credentials_impl.h \
     include/grpcpp/security/server_credentials.h \
-    include/grpcpp/security/server_credentials_impl.h \
     include/grpcpp/security/tls_credentials_options.h \
     include/grpcpp/server.h \
     include/grpcpp/server_builder.h \
@@ -5663,7 +5662,6 @@ PUBLIC_HEADERS_CXX += \
     include/grpcpp/security/credentials.h \
     include/grpcpp/security/credentials_impl.h \
     include/grpcpp/security/server_credentials.h \
-    include/grpcpp/security/server_credentials_impl.h \
     include/grpcpp/security/tls_credentials_options.h \
     include/grpcpp/server.h \
     include/grpcpp/server_builder.h \
diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml
index 0737db79647..38a4054cce5 100644
--- a/build_autogenerated.yaml
+++ b/build_autogenerated.yaml
@@ -2154,7 +2154,6 @@ libs:
   - include/grpcpp/security/credentials.h
   - include/grpcpp/security/credentials_impl.h
   - include/grpcpp/security/server_credentials.h
-  - include/grpcpp/security/server_credentials_impl.h
   - include/grpcpp/security/tls_credentials_options.h
   - include/grpcpp/server.h
   - include/grpcpp/server_builder.h
@@ -2540,7 +2539,6 @@ libs:
   - include/grpcpp/security/credentials.h
   - include/grpcpp/security/credentials_impl.h
   - include/grpcpp/security/server_credentials.h
-  - include/grpcpp/security/server_credentials_impl.h
   - include/grpcpp/security/tls_credentials_options.h
   - include/grpcpp/server.h
   - include/grpcpp/server_builder.h
diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec
index c1f21867f5e..ef8e8acff85 100644
--- a/gRPC-C++.podspec
+++ b/gRPC-C++.podspec
@@ -166,7 +166,6 @@ Pod::Spec.new do |s|
                       'include/grpcpp/security/credentials.h',
                       'include/grpcpp/security/credentials_impl.h',
                       'include/grpcpp/security/server_credentials.h',
-                      'include/grpcpp/security/server_credentials_impl.h',
                       'include/grpcpp/security/tls_credentials_options.h',
                       'include/grpcpp/server.h',
                       'include/grpcpp/server_builder.h',
diff --git a/include/grpcpp/impl/codegen/server_interface.h b/include/grpcpp/impl/codegen/server_interface.h
index 3294c4037b5..0c556f0a5f0 100644
--- a/include/grpcpp/impl/codegen/server_interface.h
+++ b/include/grpcpp/impl/codegen/server_interface.h
@@ -36,12 +36,12 @@ namespace grpc_impl {
 class Channel;
 class CompletionQueue;
 class ServerCompletionQueue;
-class ServerCredentials;
 }  // namespace grpc_impl
 namespace grpc {
 
 class AsyncGenericService;
 class GenericServerContext;
+class ServerCredentials;
 class Service;
 
 extern CoreCodegenInterface* g_core_codegen_interface;
@@ -173,7 +173,7 @@ class ServerInterface : public internal::CallHook {
   ///
   /// \warning It's an error to call this method on an already started server.
   virtual int AddListeningPort(const std::string& addr,
-                               grpc_impl::ServerCredentials* creds) = 0;
+                               ServerCredentials* creds) = 0;
 
   /// Start the server.
   ///
diff --git a/include/grpcpp/security/server_credentials.h b/include/grpcpp/security/server_credentials.h
index 5228ac138cc..d8de6b658b4 100644
--- a/include/grpcpp/security/server_credentials.h
+++ b/include/grpcpp/security/server_credentials.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2019 gRPC authors.
+ * Copyright 2015 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,71 +19,69 @@
 #ifndef GRPCPP_SECURITY_SERVER_CREDENTIALS_H
 #define GRPCPP_SECURITY_SERVER_CREDENTIALS_H
 
-#include <grpcpp/security/server_credentials_impl.h>
+#include <memory>
+#include <vector>
+
+#include <grpc/grpc_security_constants.h>
+#include <grpcpp/security/auth_metadata_processor.h>
+#include <grpcpp/security/tls_credentials_options.h>
+#include <grpcpp/support/config.h>
+
+struct grpc_server;
 
 namespace grpc_impl {
 
 class Server;
 }  // namespace grpc_impl
 namespace grpc {
-
-typedef ::grpc_impl::ServerCredentials ServerCredentials;
-
-/// Options to create ServerCredentials with SSL
-struct SslServerCredentialsOptions {
-  /// \warning Deprecated
-  SslServerCredentialsOptions()
-      : force_client_auth(false),
-        client_certificate_request(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE) {}
-  SslServerCredentialsOptions(
-      grpc_ssl_client_certificate_request_type request_type)
-      : force_client_auth(false), client_certificate_request(request_type) {}
-
-  struct PemKeyCertPair {
-    std::string private_key;
-    std::string cert_chain;
-  };
-  std::string pem_root_certs;
-  std::vector<PemKeyCertPair> pem_key_cert_pairs;
-  /// \warning Deprecated
-  bool force_client_auth;
-
-  /// If both \a force_client_auth and \a client_certificate_request
-  /// fields are set, \a force_client_auth takes effect, i.e.
-  /// \a REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
-  /// will be enforced.
-  grpc_ssl_client_certificate_request_type client_certificate_request;
+struct SslServerCredentialsOptions;
+
+/// Wrapper around \a grpc_server_credentials, a way to authenticate a server.
+class ServerCredentials {
+ public:
+  virtual ~ServerCredentials();
+
+  /// This method is not thread-safe and has to be called before the server is
+  /// started. The last call to this function wins.
+  virtual void SetAuthMetadataProcessor(
+      const std::shared_ptr<grpc::AuthMetadataProcessor>& processor) = 0;
+
+ private:
+  friend class ::grpc_impl::Server;
+
+  /// Tries to bind \a server to the given \a addr (eg, localhost:1234,
+  /// 192.168.1.1:31416, [::1]:27182, etc.)
+  ///
+  /// \return bound port number on success, 0 on failure.
+  // TODO(dgq): the "port" part seems to be a misnomer.
+  virtual int AddPortToServer(const std::string& addr, grpc_server* server) = 0;
 };
 
-static inline std::shared_ptr<ServerCredentials> SslServerCredentials(
-    const SslServerCredentialsOptions& options) {
-  return ::grpc_impl::SslServerCredentials(options);
-}
+/// Builds SSL ServerCredentials given SSL specific options
+std::shared_ptr<ServerCredentials> SslServerCredentials(
+    const grpc::SslServerCredentialsOptions& options);
 
-static inline std::shared_ptr<ServerCredentials> InsecureServerCredentials() {
-  return ::grpc_impl::InsecureServerCredentials();
-}
+/// Builds insecure server credentials.
+std::shared_ptr<ServerCredentials> InsecureServerCredentials();
 
 namespace experimental {
 
-typedef ::grpc_impl::experimental::AltsServerCredentialsOptions
-    AltsServerCredentialsOptions;
+/// Options to create ServerCredentials with ALTS
+struct AltsServerCredentialsOptions {
+  /// Add fields if needed.
+};
 
-static inline std::shared_ptr<ServerCredentials> AltsServerCredentials(
-    const AltsServerCredentialsOptions& options) {
-  return ::grpc_impl::experimental::AltsServerCredentials(options);
-}
+/// Builds ALTS ServerCredentials given ALTS specific options
+std::shared_ptr<ServerCredentials> AltsServerCredentials(
+    const AltsServerCredentialsOptions& options);
 
-static inline std::shared_ptr<ServerCredentials> LocalServerCredentials(
-    grpc_local_connect_type type) {
-  return ::grpc_impl::experimental::LocalServerCredentials(type);
-}
+/// Builds Local ServerCredentials.
+std::shared_ptr<ServerCredentials> LocalServerCredentials(
+    grpc_local_connect_type type);
 
 /// Builds TLS ServerCredentials given TLS options.
-static inline std::shared_ptr<ServerCredentials> TlsServerCredentials(
-    const ::grpc_impl::experimental::TlsCredentialsOptions& options) {
-  return ::grpc_impl::experimental::TlsServerCredentials(options);
-}
+std::shared_ptr<ServerCredentials> TlsServerCredentials(
+    const TlsCredentialsOptions& options);
 
 }  // namespace experimental
 }  // namespace grpc
diff --git a/include/grpcpp/security/server_credentials_impl.h b/include/grpcpp/security/server_credentials_impl.h
deleted file mode 100644
index 7e9e784f6e5..00000000000
--- a/include/grpcpp/security/server_credentials_impl.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- *
- * Copyright 2015 gRPC authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-#ifndef GRPCPP_SECURITY_SERVER_CREDENTIALS_IMPL_H
-#define GRPCPP_SECURITY_SERVER_CREDENTIALS_IMPL_H
-
-#include <memory>
-#include <vector>
-
-#include <grpc/grpc_security_constants.h>
-#include <grpcpp/security/auth_metadata_processor.h>
-#include <grpcpp/security/tls_credentials_options.h>
-#include <grpcpp/support/config.h>
-
-struct grpc_server;
-
-namespace grpc {
-
-struct SslServerCredentialsOptions;
-}  // namespace grpc
-namespace grpc_impl {
-class Server;
-
-/// Wrapper around \a grpc_server_credentials, a way to authenticate a server.
-class ServerCredentials {
- public:
-  virtual ~ServerCredentials();
-
-  /// This method is not thread-safe and has to be called before the server is
-  /// started. The last call to this function wins.
-  virtual void SetAuthMetadataProcessor(
-      const std::shared_ptr<grpc::AuthMetadataProcessor>& processor) = 0;
-
- private:
-  friend class ::grpc_impl::Server;
-
-  /// Tries to bind \a server to the given \a addr (eg, localhost:1234,
-  /// 192.168.1.1:31416, [::1]:27182, etc.)
-  ///
-  /// \return bound port number on success, 0 on failure.
-  // TODO(dgq): the "port" part seems to be a misnomer.
-  virtual int AddPortToServer(const std::string& addr, grpc_server* server) = 0;
-};
-
-/// Builds SSL ServerCredentials given SSL specific options
-std::shared_ptr<ServerCredentials> SslServerCredentials(
-    const grpc::SslServerCredentialsOptions& options);
-
-/// Builds insecure server credentials.
-std::shared_ptr<ServerCredentials> InsecureServerCredentials();
-
-namespace experimental {
-
-/// Options to create ServerCredentials with ALTS
-struct AltsServerCredentialsOptions {
-  /// Add fields if needed.
-};
-
-/// Builds ALTS ServerCredentials given ALTS specific options
-std::shared_ptr<ServerCredentials> AltsServerCredentials(
-    const AltsServerCredentialsOptions& options);
-
-/// Builds Local ServerCredentials.
-std::shared_ptr<ServerCredentials> LocalServerCredentials(
-    grpc_local_connect_type type);
-
-/// Builds TLS ServerCredentials given TLS options.
-std::shared_ptr<ServerCredentials> TlsServerCredentials(
-    const TlsCredentialsOptions& options);
-
-}  // namespace experimental
-}  // namespace grpc_impl
-
-#endif  // GRPCPP_SECURITY_SERVER_CREDENTIALS_IMPL_H
diff --git a/include/grpcpp/server_builder.h b/include/grpcpp/server_builder.h
index 2ebf6c2716f..8a15abad3fb 100644
--- a/include/grpcpp/server_builder.h
+++ b/include/grpcpp/server_builder.h
@@ -43,12 +43,12 @@ namespace grpc_impl {
 class CompletionQueue;
 class Server;
 class ServerCompletionQueue;
-class ServerCredentials;
 }  // namespace grpc_impl
 
 namespace grpc {
 
 class AsyncGenericService;
+class ServerCredentials;
 class Service;
 namespace testing {
 class ServerBuilderPluginTest;
@@ -130,7 +130,7 @@ class ServerBuilder {
   /// does not modify this pointer.
   ServerBuilder& AddListeningPort(
       const std::string& addr_uri,
-      std::shared_ptr<grpc_impl::ServerCredentials> creds,
+      std::shared_ptr<grpc::ServerCredentials> creds,
       int* selected_port = nullptr);
 
   /// Add a completion queue for handling asynchronous services.
@@ -312,7 +312,7 @@ class ServerBuilder {
   /// Experimental, to be deprecated
   struct Port {
     std::string addr;
-    std::shared_ptr<grpc_impl::ServerCredentials> creds;
+    std::shared_ptr<ServerCredentials> creds;
     int* selected_port;
   };
 
@@ -380,7 +380,7 @@ class ServerBuilder {
   /// List of completion queues added via \a AddCompletionQueue method.
   std::vector<grpc_impl::ServerCompletionQueue*> cqs_;
 
-  std::shared_ptr<grpc_impl::ServerCredentials> creds_;
+  std::shared_ptr<grpc::ServerCredentials> creds_;
   std::vector<std::unique_ptr<grpc::ServerBuilderPlugin>> plugins_;
   grpc_resource_quota* resource_quota_;
   grpc::AsyncGenericService* generic_service_{nullptr};
diff --git a/src/cpp/server/external_connection_acceptor_impl.h b/src/cpp/server/external_connection_acceptor_impl.h
index 3fb94c90848..9c65ac1660d 100644
--- a/src/cpp/server/external_connection_acceptor_impl.h
+++ b/src/cpp/server/external_connection_acceptor_impl.h
@@ -23,7 +23,6 @@
 
 #include <grpc/impl/codegen/grpc_types.h>
 #include <grpcpp/security/server_credentials.h>
-#include <grpcpp/security/server_credentials_impl.h>
 #include <grpcpp/server_builder.h>
 #include <grpcpp/support/channel_arguments.h>
 
diff --git a/src/cpp/server/insecure_server_credentials.cc b/src/cpp/server/insecure_server_credentials.cc
index bc908920b8d..04e5435efb9 100644
--- a/src/cpp/server/insecure_server_credentials.cc
+++ b/src/cpp/server/insecure_server_credentials.cc
@@ -21,7 +21,7 @@
 #include <grpc/grpc.h>
 #include <grpc/support/log.h>
 
-namespace grpc_impl {
+namespace grpc {
 namespace {
 class InsecureServerCredentialsImpl final : public ServerCredentials {
  public:
@@ -41,4 +41,4 @@ std::shared_ptr<ServerCredentials> InsecureServerCredentials() {
       new InsecureServerCredentialsImpl());
 }
 
-}  // namespace grpc_impl
+}  // namespace grpc
diff --git a/src/cpp/server/secure_server_credentials.cc b/src/cpp/server/secure_server_credentials.cc
index f94696fd767..6ca31ee0843 100644
--- a/src/cpp/server/secure_server_credentials.cc
+++ b/src/cpp/server/secure_server_credentials.cc
@@ -92,10 +92,6 @@ void AuthMetadataProcessorAyncWrapper::InvokeProcessor(
      status.error_message().c_str());
 }
 
-}  // namespace grpc
-
-namespace grpc_impl {
-
 int SecureServerCredentials::AddPortToServer(const std::string& addr,
                                              grpc_server* server) {
   return grpc_server_add_secure_http2_port(server, addr.c_str(), creds_);
@@ -156,4 +152,4 @@ std::shared_ptr<ServerCredentials> TlsServerCredentials(
 }
 
 }  // namespace experimental
-}  // namespace grpc_impl
+}  // namespace grpc
diff --git a/src/cpp/server/secure_server_credentials.h b/src/cpp/server/secure_server_credentials.h
index 9e9e33579f0..407d70766b5 100644
--- a/src/cpp/server/secure_server_credentials.h
+++ b/src/cpp/server/secure_server_credentials.h
@@ -28,14 +28,9 @@
 
 #include "src/cpp/server/thread_pool_interface.h"
 
-namespace grpc_impl {
-
-class SecureServerCredentials;
-}  // namespace grpc_impl
-
 namespace grpc {
 
-typedef ::grpc_impl::SecureServerCredentials SecureServerCredentials;
+class SecureServerCredentials;
 
 class AuthMetadataProcessorAyncWrapper final {
  public:
@@ -61,10 +56,6 @@ class AuthMetadataProcessorAyncWrapper final {
   std::shared_ptr<AuthMetadataProcessor> processor_;
 };
 
-}  // namespace grpc
-
-namespace grpc_impl {
-
 class SecureServerCredentials final : public ServerCredentials {
  public:
   explicit SecureServerCredentials(grpc_server_credentials* creds)
@@ -83,6 +74,6 @@ class SecureServerCredentials final : public ServerCredentials {
   std::unique_ptr<grpc::AuthMetadataProcessorAyncWrapper> processor_;
 };
 
-}  // namespace grpc_impl
+}  // namespace grpc
 
 #endif  // GRPC_INTERNAL_CPP_SERVER_SECURE_SERVER_CREDENTIALS_H
diff --git a/src/cpp/server/server_credentials.cc b/src/cpp/server/server_credentials.cc
index 8b85264f9d7..c3b3a8b3793 100644
--- a/src/cpp/server/server_credentials.cc
+++ b/src/cpp/server/server_credentials.cc
@@ -16,10 +16,10 @@
  *
  */
 
-#include <grpcpp/security/server_credentials_impl.h>
+#include <grpcpp/security/server_credentials.h>
 
-namespace grpc_impl {
+namespace grpc {
 
 ServerCredentials::~ServerCredentials() {}
 
-}  // namespace grpc_impl
+}  // namespace grpc
diff --git a/tools/doxygen/Doxyfile.c++ b/tools/doxygen/Doxyfile.c++
index 3ce6109b49d..ac34b49e30b 100644
--- a/tools/doxygen/Doxyfile.c++
+++ b/tools/doxygen/Doxyfile.c++
@@ -1026,7 +1026,6 @@ include/grpcpp/security/auth_metadata_processor.h \
 include/grpcpp/security/credentials.h \
 include/grpcpp/security/credentials_impl.h \
 include/grpcpp/security/server_credentials.h \
-include/grpcpp/security/server_credentials_impl.h \
 include/grpcpp/security/tls_credentials_options.h \
 include/grpcpp/server.h \
 include/grpcpp/server_builder.h \
diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal
index 2dd2cb0c123..5be2623a948 100644
--- a/tools/doxygen/Doxyfile.c++.internal
+++ b/tools/doxygen/Doxyfile.c++.internal
@@ -1026,7 +1026,6 @@ include/grpcpp/security/auth_metadata_processor.h \
 include/grpcpp/security/credentials.h \
 include/grpcpp/security/credentials_impl.h \
 include/grpcpp/security/server_credentials.h \
-include/grpcpp/security/server_credentials_impl.h \
 include/grpcpp/security/tls_credentials_options.h \
 include/grpcpp/server.h \
 include/grpcpp/server_builder.h \

From db3cd51b2f1ef2f07cea58a1b53cb1107c28898b Mon Sep 17 00:00:00 2001
From: Karthik Ravi Shankar <karthikrs@google.com>
Date: Thu, 23 Jul 2020 16:58:51 -0700
Subject: [PATCH 03/38] Move ErrorDetails from ::grpc_impl to ::grpc

Reverts: https://github.com/grpc/grpc/pull/18455
---
 BUILD                                  |  1 -
 CMakeLists.txt                         |  1 -
 Makefile                               |  1 -
 include/grpcpp/support/error_details.h | 28 +++++++++++++++-----------
 src/cpp/util/error_details.cc          |  4 ++--
 5 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/BUILD b/BUILD
index 036c782ac30..d034bfc42e6 100644
--- a/BUILD
+++ b/BUILD
@@ -410,7 +410,6 @@ grpc_cc_library(
     hdrs = [
         "include/grpc++/support/error_details.h",
         "include/grpcpp/support/error_details.h",
-        "include/grpcpp/support/error_details_impl.h",
     ],
     language = "c++",
     standalone = True,
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1b85e61f25a..7a160c32cb8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2991,7 +2991,6 @@ target_link_libraries(grpc++_error_details
 foreach(_hdr
   include/grpc++/support/error_details.h
   include/grpcpp/support/error_details.h
-  include/grpcpp/support/error_details_impl.h
 )
   string(REPLACE "include/" "" _path ${_hdr})
   get_filename_component(_path ${_path} PATH)
diff --git a/Makefile b/Makefile
index 40e2642512c..943ba0ceab3 100644
--- a/Makefile
+++ b/Makefile
@@ -5148,7 +5148,6 @@ LIBGRPC++_ERROR_DETAILS_SRC = \
 PUBLIC_HEADERS_CXX += \
     include/grpc++/support/error_details.h \
     include/grpcpp/support/error_details.h \
-    include/grpcpp/support/error_details_impl.h \
 
 LIBGRPC++_ERROR_DETAILS_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC++_ERROR_DETAILS_SRC))))
 
diff --git a/include/grpcpp/support/error_details.h b/include/grpcpp/support/error_details.h
index 07bc750db5c..acf791a3582 100644
--- a/include/grpcpp/support/error_details.h
+++ b/include/grpcpp/support/error_details.h
@@ -16,10 +16,10 @@
  *
  */
 
-#ifndef GRPCPP_SUPPORT_ERROR_DETAILS_H
-#define GRPCPP_SUPPORT_ERROR_DETAILS_H
+#ifndef GRPCPP_SUPPORT_ERROR_DETAILS_IMPL_H
+#define GRPCPP_SUPPORT_ERROR_DETAILS_IMPL_H
 
-#include <grpcpp/support/error_details_impl.h>
+#include <grpcpp/support/status.h>
 
 namespace google {
 namespace rpc {
@@ -29,16 +29,20 @@ class Status;
 
 namespace grpc {
 
-static inline Status ExtractErrorDetails(const Status& from,
-                                         ::google::rpc::Status* to) {
-  return ::grpc_impl::ExtractErrorDetails(from, to);
-}
+/// Map a \a grpc::Status to a \a google::rpc::Status.
+/// The given \a to object will be cleared.
+/// On success, returns status with OK.
+/// Returns status with \a INVALID_ARGUMENT, if failed to deserialize.
+/// Returns status with \a FAILED_PRECONDITION, if \a to is nullptr.
+grpc::Status ExtractErrorDetails(const grpc::Status& from,
+                                 ::google::rpc::Status* to);
 
-static inline Status SetErrorDetails(const ::google::rpc::Status& from,
-                                     Status* to) {
-  return ::grpc_impl::SetErrorDetails(from, to);
-}
+/// Map \a google::rpc::Status to a \a grpc::Status.
+/// Returns OK on success.
+/// Returns status with \a FAILED_PRECONDITION if \a to is nullptr.
+grpc::Status SetErrorDetails(const ::google::rpc::Status& from,
+                             grpc::Status* to);
 
 }  // namespace grpc
 
-#endif  // GRPCPP_SUPPORT_ERROR_DETAILS_H
+#endif  // GRPCPP_SUPPORT_ERROR_DETAILS_IMPL_H
diff --git a/src/cpp/util/error_details.cc b/src/cpp/util/error_details.cc
index a1aafcbdc6e..f35a6125552 100644
--- a/src/cpp/util/error_details.cc
+++ b/src/cpp/util/error_details.cc
@@ -20,7 +20,7 @@
 
 #include "src/proto/grpc/status/status.pb.h"
 
-namespace grpc_impl {
+namespace grpc {
 
 grpc::Status ExtractErrorDetails(const grpc::Status& from,
                                  ::google::rpc::Status* to) {
@@ -47,4 +47,4 @@ grpc::Status SetErrorDetails(const ::google::rpc::Status& from,
   return grpc::Status::OK;
 }
 
-}  // namespace grpc_impl
+}  // namespace grpc

From 44dd19d8da3fa262dadb8e8ebe1897e6815d1d89 Mon Sep 17 00:00:00 2001
From: "Mark D. Roth" <roth@google.com>
Date: Mon, 27 Jul 2020 10:25:05 -0700
Subject: [PATCH 04/38] Pass repo manager duty to nicolasnoble.

---
 .github/ISSUE_TEMPLATE/bug_report.md      | 2 +-
 .github/ISSUE_TEMPLATE/cleanup_request.md | 2 +-
 .github/ISSUE_TEMPLATE/feature_request.md | 2 +-
 .github/ISSUE_TEMPLATE/question.md        | 2 +-
 .github/pull_request_template.md          | 2 +-
 5 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 41f81be1db6..e69dc0bd3ba 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -2,7 +2,7 @@
 name: Report a bug
 about: Create a report to help us improve
 labels: kind/bug, priority/P2
-assignees: markdroth
+assignees: nicolasnoble
 
 ---
 
diff --git a/.github/ISSUE_TEMPLATE/cleanup_request.md b/.github/ISSUE_TEMPLATE/cleanup_request.md
index 7da478019fc..c9a6d3f911d 100644
--- a/.github/ISSUE_TEMPLATE/cleanup_request.md
+++ b/.github/ISSUE_TEMPLATE/cleanup_request.md
@@ -2,7 +2,7 @@
 name: Request a cleanup
 about: Suggest a cleanup in our repository
 labels: kind/internal cleanup, priority/P2
-assignees: markdroth
+assignees: nicolasnoble
 
 ---
 
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index 00599d145d8..e3137998ae0 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -2,7 +2,7 @@
 name: Request a feature
 about: Suggest an idea for this project
 labels: kind/enhancement, priority/P2
-assignees: markdroth
+assignees: nicolasnoble
 
 ---
 
diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md
index 5dc067381f9..cfde18bec27 100644
--- a/.github/ISSUE_TEMPLATE/question.md
+++ b/.github/ISSUE_TEMPLATE/question.md
@@ -2,7 +2,7 @@
 name: Ask a question
 about: Ask a question
 labels: kind/question, priority/P3
-assignees: markdroth
+assignees: nicolasnoble
 
 ---
 
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index a85cfad9c7a..57af6c21597 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -8,4 +8,4 @@ If you know who should review your pull request, please remove the mentioning be
 
 -->
 
-@markdroth
+@nicolasnoble

From dbec2415bfcf8a6901148b3ae3ba21a7022ed760 Mon Sep 17 00:00:00 2001
From: Karthik Ravi Shankar <karthikrs@google.com>
Date: Mon, 27 Jul 2020 11:37:19 -0700
Subject: [PATCH 05/38] More local fixes

---
 include/grpcpp/security/server_credentials.h | 2 +-
 src/cpp/server/secure_server_credentials.cc  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/include/grpcpp/security/server_credentials.h b/include/grpcpp/security/server_credentials.h
index d8de6b658b4..e6d4daab422 100644
--- a/include/grpcpp/security/server_credentials.h
+++ b/include/grpcpp/security/server_credentials.h
@@ -81,7 +81,7 @@ std::shared_ptr<ServerCredentials> LocalServerCredentials(
 
 /// Builds TLS ServerCredentials given TLS options.
 std::shared_ptr<ServerCredentials> TlsServerCredentials(
-    const TlsCredentialsOptions& options);
+    const ::grpc_impl::experimental::TlsCredentialsOptions& options);
 
 }  // namespace experimental
 }  // namespace grpc
diff --git a/src/cpp/server/secure_server_credentials.cc b/src/cpp/server/secure_server_credentials.cc
index 6ca31ee0843..b4ba7153435 100644
--- a/src/cpp/server/secure_server_credentials.cc
+++ b/src/cpp/server/secure_server_credentials.cc
@@ -145,7 +145,7 @@ std::shared_ptr<ServerCredentials> LocalServerCredentials(
 }
 
 std::shared_ptr<ServerCredentials> TlsServerCredentials(
-    const TlsCredentialsOptions& options) {
+    const grpc_impl::experimental::TlsCredentialsOptions& options) {
   grpc::GrpcLibraryCodegen init;
   return std::shared_ptr<ServerCredentials>(new SecureServerCredentials(
       grpc_tls_server_credentials_create(options.c_credentials_options())));

From 968e1b40a540db1d66e9cb49cad273b0c64ffd4e Mon Sep 17 00:00:00 2001
From: Vijay Pai <vpai@google.com>
Date: Tue, 28 Jul 2020 12:42:20 -0700
Subject: [PATCH 06/38] Properly follow callback API guarantees rather than
 existing behavior

---
 test/cpp/microbenchmarks/bm_cq.cc | 62 ++++++++++++++++++++++++++++++-
 1 file changed, 60 insertions(+), 2 deletions(-)

diff --git a/test/cpp/microbenchmarks/bm_cq.cc b/test/cpp/microbenchmarks/bm_cq.cc
index c53eb2b9413..1640c4d52f0 100644
--- a/test/cpp/microbenchmarks/bm_cq.cc
+++ b/test/cpp/microbenchmarks/bm_cq.cc
@@ -69,6 +69,11 @@ BENCHMARK(BM_CreateDestroyCore);
 static void DoneWithCompletionOnStack(void* /*arg*/,
                                       grpc_cq_completion* /*completion*/) {}
 
+static void DoneWithCompletionOnHeap(void* /*arg*/,
+                                     grpc_cq_completion* completion) {
+  delete completion;
+}
+
 class DummyTag final : public internal::CompletionQueueTag {
  public:
   bool FinalizeResult(void** /*tag*/, bool* /*status*/) override {
@@ -205,8 +210,15 @@ static void BM_Callback_CQ_Pass1Core(benchmark::State& state) {
   gpr_cv_init(&shutdown_cv);
   bool got_shutdown = false;
   ShutdownCallback shutdown_cb(&got_shutdown);
-  grpc_completion_queue* cc =
-      grpc_completion_queue_create_for_callback(&shutdown_cb, nullptr);
+  // This test with stack-allocated completions only works for non-polling or
+  // EM-polling callback core CQs. For generality, test with non-polling.
+  grpc_completion_queue_attributes attr;
+  attr.version = 2;
+  attr.cq_completion_type = GRPC_CQ_CALLBACK;
+  attr.cq_polling_type = GRPC_CQ_NON_POLLING;
+  attr.cq_shutdown_cb = &shutdown_cb;
+  grpc_completion_queue* cc = grpc_completion_queue_create(
+      grpc_completion_queue_factory_lookup(&attr), &attr, nullptr);
   for (auto _ : state) {
     grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
     grpc_core::ExecCtx exec_ctx;
@@ -240,7 +252,53 @@ static void BM_Callback_CQ_Pass1Core(benchmark::State& state) {
   gpr_cv_destroy(&shutdown_cv);
   gpr_mu_destroy(&shutdown_mu);
 }
+static void BM_Callback_CQ_Pass1CoreHeapCompletion(benchmark::State& state) {
+  TrackCounters track_counters;
+  int iteration = 0, current_iterations = 0;
+  TagCallback tag_cb(&iteration);
+  gpr_mu_init(&mu);
+  gpr_cv_init(&cv);
+  gpr_mu_init(&shutdown_mu);
+  gpr_cv_init(&shutdown_cv);
+  bool got_shutdown = false;
+  ShutdownCallback shutdown_cb(&got_shutdown);
+  grpc_completion_queue* cc =
+      grpc_completion_queue_create_for_callback(&shutdown_cb, nullptr);
+  for (auto _ : state) {
+    grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
+    grpc_core::ExecCtx exec_ctx;
+    grpc_cq_completion* completion = new grpc_cq_completion;
+    GPR_ASSERT(grpc_cq_begin_op(cc, &tag_cb));
+    grpc_cq_end_op(cc, &tag_cb, GRPC_ERROR_NONE, DoneWithCompletionOnHeap,
+                   nullptr, completion);
+  }
+  shutdown_and_destroy(cc);
+
+  gpr_mu_lock(&mu);
+  current_iterations = static_cast<int>(state.iterations());
+  while (current_iterations != iteration) {
+    // Wait for all the callbacks to complete.
+    gpr_cv_wait(&cv, &mu, gpr_inf_future(GPR_CLOCK_REALTIME));
+  }
+  gpr_mu_unlock(&mu);
+
+  gpr_mu_lock(&shutdown_mu);
+  while (!got_shutdown) {
+    // Wait for the shutdown callback to complete.
+    gpr_cv_wait(&shutdown_cv, &shutdown_mu, gpr_inf_future(GPR_CLOCK_REALTIME));
+  }
+  gpr_mu_unlock(&shutdown_mu);
+
+  GPR_ASSERT(got_shutdown);
+  GPR_ASSERT(iteration == static_cast<int>(state.iterations()));
+  track_counters.Finish(state);
+  gpr_cv_destroy(&cv);
+  gpr_mu_destroy(&mu);
+  gpr_cv_destroy(&shutdown_cv);
+  gpr_mu_destroy(&shutdown_mu);
+}
 BENCHMARK(BM_Callback_CQ_Pass1Core);
+BENCHMARK(BM_Callback_CQ_Pass1CoreHeapCompletion);
 
 }  // namespace testing
 }  // namespace grpc

From ac0f7e80eafd8ccacc3235767f8681ffebb53a93 Mon Sep 17 00:00:00 2001
From: Stanley Cheung <stanleycheung@google.com>
Date: Tue, 28 Jul 2020 22:35:42 -0700
Subject: [PATCH 07/38] Attempt to de-flake dart interop build

---
 .../interoptest/grpc_interop_dart/build_interop.sh.template    | 3 ++-
 .../dockerfile/interoptest/grpc_interop_dart/build_interop.sh  | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/templates/tools/dockerfile/interoptest/grpc_interop_dart/build_interop.sh.template b/templates/tools/dockerfile/interoptest/grpc_interop_dart/build_interop.sh.template
index f5c48593b9a..c3a9c7fb892 100644
--- a/templates/tools/dockerfile/interoptest/grpc_interop_dart/build_interop.sh.template
+++ b/templates/tools/dockerfile/interoptest/grpc_interop_dart/build_interop.sh.template
@@ -25,4 +25,5 @@
   cp -r /var/local/jenkins/service_account $HOME || true
 
   cd /var/local/git/grpc-dart/interop
-  /usr/lib/dart/bin/pub get --verbose
+  # De-flake attempt: run the cmd one more time in case of transient failure
+  /usr/lib/dart/bin/pub get --verbose || /usr/lib/dart/bin/pub get --verbose
diff --git a/tools/dockerfile/interoptest/grpc_interop_dart/build_interop.sh b/tools/dockerfile/interoptest/grpc_interop_dart/build_interop.sh
index 589983ce58c..2b4c07ed128 100644
--- a/tools/dockerfile/interoptest/grpc_interop_dart/build_interop.sh
+++ b/tools/dockerfile/interoptest/grpc_interop_dart/build_interop.sh
@@ -23,4 +23,5 @@ git clone /var/local/jenkins/grpc-dart /var/local/git/grpc-dart
 cp -r /var/local/jenkins/service_account $HOME || true
 
 cd /var/local/git/grpc-dart/interop
-/usr/lib/dart/bin/pub get --verbose
+# De-flake attempt: run the cmd one more time in case of transient failure
+/usr/lib/dart/bin/pub get --verbose || /usr/lib/dart/bin/pub get --verbose

From 26fadb15c402329739fbbdbc6c919ae5f6bcbefd Mon Sep 17 00:00:00 2001
From: Karthik Ravi Shankar <karthikrs@google.com>
Date: Wed, 29 Jul 2020 09:59:20 -0700
Subject: [PATCH 08/38] Fix header guards

---
 include/grpcpp/support/error_details.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/include/grpcpp/support/error_details.h b/include/grpcpp/support/error_details.h
index acf791a3582..15b917f6c5c 100644
--- a/include/grpcpp/support/error_details.h
+++ b/include/grpcpp/support/error_details.h
@@ -16,8 +16,8 @@
  *
  */
 
-#ifndef GRPCPP_SUPPORT_ERROR_DETAILS_IMPL_H
-#define GRPCPP_SUPPORT_ERROR_DETAILS_IMPL_H
+#ifndef GRPCPP_SUPPORT_ERROR_DETAILS_H
+#define GRPCPP_SUPPORT_ERROR_DETAILS_H
 
 #include <grpcpp/support/status.h>
 
@@ -45,4 +45,4 @@ grpc::Status SetErrorDetails(const ::google::rpc::Status& from,
 
 }  // namespace grpc
 
-#endif  // GRPCPP_SUPPORT_ERROR_DETAILS_IMPL_H
+#endif  // GRPCPP_SUPPORT_ERROR_DETAILS_H

From c6586f087f615d96f0f3233255b6f8944565e681 Mon Sep 17 00:00:00 2001
From: Michael Wang <mywang@google.com>
Date: Wed, 29 Jul 2020 19:39:22 +0000
Subject: [PATCH 09/38] implemented get_local_address accessor for
 grpc_endpoint

---
 .../client_channel/http_connect_handshaker.cc |  5 +-
 .../chttp2/transport/chttp2_transport.cc      | 41 ++++++++--------
 .../chttp2/transport/flow_control.cc          |  2 +-
 .../chttp2/transport/frame_settings.cc        |  4 +-
 .../ext/transport/chttp2/transport/internal.h |  2 +-
 .../ext/transport/chttp2/transport/writing.cc | 11 +++--
 src/core/lib/iomgr/endpoint.cc                |  6 ++-
 src/core/lib/iomgr/endpoint.h                 |  9 +++-
 src/core/lib/iomgr/endpoint_cfstream.cc       | 43 ++++++++++++-----
 src/core/lib/iomgr/tcp_custom.cc              | 48 ++++++++++++-------
 src/core/lib/iomgr/tcp_posix.cc               | 44 ++++++++++++-----
 src/core/lib/iomgr/tcp_windows.cc             | 36 ++++++++++----
 .../lib/security/transport/secure_endpoint.cc |  8 +++-
 test/core/util/mock_endpoint.cc               |  9 +++-
 test/core/util/passthru_endpoint.cc           | 14 ++++--
 test/core/util/reconnect_server.cc            | 30 ++++++------
 test/core/util/reconnect_server.h             |  2 +-
 test/core/util/trickle_endpoint.cc            |  8 +++-
 test/cpp/end2end/port_sharing_end2end_test.cc |  5 +-
 .../microbenchmarks/bm_chttp2_transport.cc    |  6 ++-
 20 files changed, 219 insertions(+), 114 deletions(-)

diff --git a/src/core/ext/filters/client_channel/http_connect_handshaker.cc b/src/core/ext/filters/client_channel/http_connect_handshaker.cc
index 3f60f0ff373..620cc89e334 100644
--- a/src/core/ext/filters/client_channel/http_connect_handshaker.cc
+++ b/src/core/ext/filters/client_channel/http_connect_handshaker.cc
@@ -325,10 +325,9 @@ void HttpConnectHandshaker::DoHandshake(grpc_tcp_server_acceptor* /*acceptor*/,
   args_ = args;
   on_handshake_done_ = on_handshake_done;
   // Log connection via proxy.
-  char* proxy_name = grpc_endpoint_get_peer(args->endpoint);
+  std::string proxy_name(grpc_endpoint_get_peer(args->endpoint));
   gpr_log(GPR_INFO, "Connecting to server %s via HTTP proxy %s", server_name,
-          proxy_name);
-  gpr_free(proxy_name);
+          proxy_name.c_str());
   // Construct HTTP CONNECT request.
   grpc_httpcli_request request;
   request.host = server_name;
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
index ddaed426bf6..adab27a2c2a 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
@@ -226,7 +226,6 @@ grpc_chttp2_transport::~grpc_chttp2_transport() {
 
   GRPC_ERROR_UNREF(closed_with_error);
   gpr_free(ping_acks);
-  gpr_free(peer_string);
 }
 
 static const grpc_transport_vtable* get_vtable(void);
@@ -378,11 +377,9 @@ static bool read_channel_args(grpc_chttp2_transport* t,
     }
   }
   if (channelz_enabled) {
-    // TODO(ncteisen): add an API to endpoint to query for local addr, and pass
-    // it in here, so SocketNode knows its own address.
     t->channelz_socket =
         grpc_core::MakeRefCounted<grpc_core::channelz::SocketNode>(
-            "", t->peer_string,
+            std::string(grpc_endpoint_get_local_address(t->ep)), t->peer_string,
             absl::StrFormat("%s %s", get_vtable()->name, t->peer_string));
   }
   return enable_bdp;
@@ -795,7 +792,7 @@ static void set_write_state(grpc_chttp2_transport* t,
                             grpc_chttp2_write_state st, const char* reason) {
   GRPC_CHTTP2_IF_TRACING(
       gpr_log(GPR_INFO, "W:%p %s [%s] state %s -> %s [%s]", t,
-              t->is_client ? "CLIENT" : "SERVER", t->peer_string,
+              t->is_client ? "CLIENT" : "SERVER", t->peer_string.c_str(),
               write_state_name(t->write_state), write_state_name(st), reason));
   t->write_state = st;
   // If the state is being reset back to idle, it means a write was just
@@ -1084,7 +1081,7 @@ void grpc_chttp2_add_incoming_goaway(grpc_chttp2_transport* t,
   // We want to log this irrespective of whether http tracing is enabled if we
   // received a GOAWAY with a non NO_ERROR code.
   if (goaway_error != GRPC_HTTP2_NO_ERROR) {
-    gpr_log(GPR_INFO, "%s: Got goaway [%d] err=%s", t->peer_string,
+    gpr_log(GPR_INFO, "%s: Got goaway [%d] err=%s", t->peer_string.c_str(),
             goaway_error, grpc_error_string(t->goaway_error));
   }
   // When a client receives a GOAWAY with error code ENHANCE_YOUR_CALM and debug
@@ -1216,7 +1213,7 @@ void grpc_chttp2_complete_closure_step(grpc_chttp2_transport* t,
           "Error in HTTP transport completing operation");
       closure->error_data.error = grpc_error_set_str(
           closure->error_data.error, GRPC_ERROR_STR_TARGET_ADDRESS,
-          grpc_slice_from_copied_string(t->peer_string));
+          grpc_slice_from_copied_string(t->peer_string.c_str()));
     }
     closure->error_data.error =
         grpc_error_add_child(closure->error_data.error, error);
@@ -1474,7 +1471,7 @@ static void perform_stream_op_locked(void* stream_op,
     }
     if (op_payload->send_initial_metadata.peer_string != nullptr) {
       gpr_atm_rel_store(op_payload->send_initial_metadata.peer_string,
-                        (gpr_atm)t->peer_string);
+                        (gpr_atm)t->peer_string.c_str());
     }
   }
 
@@ -1587,7 +1584,7 @@ static void perform_stream_op_locked(void* stream_op,
         op_payload->recv_initial_metadata.trailing_metadata_available;
     if (op_payload->recv_initial_metadata.peer_string != nullptr) {
       gpr_atm_rel_store(op_payload->recv_initial_metadata.peer_string,
-                        (gpr_atm)t->peer_string);
+                        (gpr_atm)t->peer_string.c_str());
     }
     grpc_chttp2_maybe_complete_recv_initial_metadata(t, s);
   }
@@ -1755,9 +1752,8 @@ static void retry_initiate_ping_locked(void* tp, grpc_error* error) {
 void grpc_chttp2_ack_ping(grpc_chttp2_transport* t, uint64_t id) {
   grpc_chttp2_ping_queue* pq = &t->ping_queue;
   if (pq->inflight_id != id) {
-    char* from = grpc_endpoint_get_peer(t->ep);
-    gpr_log(GPR_DEBUG, "Unknown ping response from %s: %" PRIx64, from, id);
-    gpr_free(from);
+    gpr_log(GPR_DEBUG, "Unknown ping response from %s: %" PRIx64,
+            t->peer_string.c_str(), id);
     return;
   }
   grpc_core::ExecCtx::RunList(DEBUG_LOCATION,
@@ -1769,7 +1765,7 @@ void grpc_chttp2_ack_ping(grpc_chttp2_transport* t, uint64_t id) {
 
 static void send_goaway(grpc_chttp2_transport* t, grpc_error* error) {
   // We want to log this irrespective of whether http tracing is enabled
-  gpr_log(GPR_INFO, "%s: Sending goaway err=%s", t->peer_string,
+  gpr_log(GPR_INFO, "%s: Sending goaway err=%s", t->peer_string.c_str(),
           grpc_error_string(error));
   t->sent_goaway_state = GRPC_CHTTP2_GOAWAY_SEND_SCHEDULED;
   grpc_http2_error_code http_error;
@@ -2641,7 +2637,7 @@ static void start_bdp_ping(void* tp, grpc_error* error) {
 static void start_bdp_ping_locked(void* tp, grpc_error* error) {
   grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(tp);
   if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
-    gpr_log(GPR_INFO, "%s: Start BDP ping err=%s", t->peer_string,
+    gpr_log(GPR_INFO, "%s: Start BDP ping err=%s", t->peer_string.c_str(),
             grpc_error_string(error));
   }
   if (error != GRPC_ERROR_NONE || t->closed_with_error != GRPC_ERROR_NONE) {
@@ -2665,7 +2661,7 @@ static void finish_bdp_ping(void* tp, grpc_error* error) {
 static void finish_bdp_ping_locked(void* tp, grpc_error* error) {
   grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(tp);
   if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
-    gpr_log(GPR_INFO, "%s: Complete BDP ping err=%s", t->peer_string,
+    gpr_log(GPR_INFO, "%s: Complete BDP ping err=%s", t->peer_string.c_str(),
             grpc_error_string(error));
   }
   if (error != GRPC_ERROR_NONE || t->closed_with_error != GRPC_ERROR_NONE) {
@@ -2835,7 +2831,7 @@ static void start_keepalive_ping_locked(void* arg, grpc_error* error) {
   }
   if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace) ||
       GRPC_TRACE_FLAG_ENABLED(grpc_keepalive_trace)) {
-    gpr_log(GPR_INFO, "%s: Start keepalive ping", t->peer_string);
+    gpr_log(GPR_INFO, "%s: Start keepalive ping", t->peer_string.c_str());
   }
   GRPC_CHTTP2_REF_TRANSPORT(t, "keepalive watchdog");
   GRPC_CLOSURE_INIT(&t->keepalive_watchdog_fired_locked,
@@ -2859,7 +2855,7 @@ static void finish_keepalive_ping_locked(void* arg, grpc_error* error) {
     if (error == GRPC_ERROR_NONE) {
       if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace) ||
           GRPC_TRACE_FLAG_ENABLED(grpc_keepalive_trace)) {
-        gpr_log(GPR_INFO, "%s: Finish keepalive ping", t->peer_string);
+        gpr_log(GPR_INFO, "%s: Finish keepalive ping", t->peer_string.c_str());
       }
       if (!t->keepalive_ping_started) {
         // start_keepalive_ping_locked has not run yet. Reschedule
@@ -2897,7 +2893,7 @@ static void keepalive_watchdog_fired_locked(void* arg, grpc_error* error) {
   if (t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_PINGING) {
     if (error == GRPC_ERROR_NONE) {
       gpr_log(GPR_INFO, "%s: Keepalive watchdog fired. Closing transport.",
-              t->peer_string);
+              t->peer_string.c_str());
       t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_DYING;
       close_transport_locked(
           t, grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
@@ -3205,7 +3201,7 @@ static void benign_reclaimer_locked(void* arg, grpc_error* error) {
     // disconnect cleanly
     if (GRPC_TRACE_FLAG_ENABLED(grpc_resource_quota_trace)) {
       gpr_log(GPR_INFO, "HTTP2: %s - send goaway to free memory",
-              t->peer_string);
+              t->peer_string.c_str());
     }
     send_goaway(t,
                 grpc_error_set_int(
@@ -3216,7 +3212,8 @@ static void benign_reclaimer_locked(void* arg, grpc_error* error) {
     gpr_log(GPR_INFO,
             "HTTP2: %s - skip benign reclamation, there are still %" PRIdPTR
             " streams",
-            t->peer_string, grpc_chttp2_stream_map_size(&t->stream_map));
+            t->peer_string.c_str(),
+            grpc_chttp2_stream_map_size(&t->stream_map));
   }
   t->benign_reclaimer_registered = false;
   if (error != GRPC_ERROR_CANCELLED) {
@@ -3241,8 +3238,8 @@ static void destructive_reclaimer_locked(void* arg, grpc_error* error) {
     grpc_chttp2_stream* s = static_cast<grpc_chttp2_stream*>(
         grpc_chttp2_stream_map_rand(&t->stream_map));
     if (GRPC_TRACE_FLAG_ENABLED(grpc_resource_quota_trace)) {
-      gpr_log(GPR_INFO, "HTTP2: %s - abandon stream id %d", t->peer_string,
-              s->id);
+      gpr_log(GPR_INFO, "HTTP2: %s - abandon stream id %d",
+              t->peer_string.c_str(), s->id);
     }
     grpc_chttp2_cancel_stream(
         t, s,
diff --git a/src/core/ext/transport/chttp2/transport/flow_control.cc b/src/core/ext/transport/chttp2/transport/flow_control.cc
index f2b2bbd3920..e54744c1a75 100644
--- a/src/core/ext/transport/chttp2/transport/flow_control.cc
+++ b/src/core/ext/transport/chttp2/transport/flow_control.cc
@@ -175,7 +175,7 @@ TransportFlowControl::TransportFlowControl(const grpc_chttp2_transport* t,
                                            bool enable_bdp_probe)
     : t_(t),
       enable_bdp_probe_(enable_bdp_probe),
-      bdp_estimator_(t->peer_string),
+      bdp_estimator_(t->peer_string.c_str()),
       pid_controller_(grpc_core::PidController::Args()
                           .set_gain_p(4)
                           .set_gain_i(8)
diff --git a/src/core/ext/transport/chttp2/transport/frame_settings.cc b/src/core/ext/transport/chttp2/transport/frame_settings.cc
index 647f52c10ef..7dbfc69ea0d 100644
--- a/src/core/ext/transport/chttp2/transport/frame_settings.cc
+++ b/src/core/ext/transport/chttp2/transport/frame_settings.cc
@@ -228,8 +228,8 @@ grpc_error* grpc_chttp2_settings_parser_parse(void* p, grpc_chttp2_transport* t,
           parser->incoming_settings[id] = parser->value;
           if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
             gpr_log(GPR_INFO, "CHTTP2:%s:%s: got setting %s = %d",
-                    t->is_client ? "CLI" : "SVR", t->peer_string, sp->name,
-                    parser->value);
+                    t->is_client ? "CLI" : "SVR", t->peer_string.c_str(),
+                    sp->name, parser->value);
           }
         } else if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
           gpr_log(GPR_ERROR, "CHTTP2: Ignoring unknown setting %d (value %d)",
diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h
index 34589e0e340..28da24f647d 100644
--- a/src/core/ext/transport/chttp2/transport/internal.h
+++ b/src/core/ext/transport/chttp2/transport/internal.h
@@ -290,7 +290,7 @@ struct grpc_chttp2_transport {
   grpc_transport base; /* must be first */
   grpc_core::RefCount refs;
   grpc_endpoint* ep;
-  char* peer_string;
+  std::string peer_string;
 
   grpc_resource_user* resource_user;
 
diff --git a/src/core/ext/transport/chttp2/transport/writing.cc b/src/core/ext/transport/chttp2/transport/writing.cc
index 5948ac98f96..f8e21e8e159 100644
--- a/src/core/ext/transport/chttp2/transport/writing.cc
+++ b/src/core/ext/transport/chttp2/transport/writing.cc
@@ -58,7 +58,7 @@ static void maybe_initiate_ping(grpc_chttp2_transport* t) {
         GRPC_TRACE_FLAG_ENABLED(grpc_bdp_estimator_trace) ||
         GRPC_TRACE_FLAG_ENABLED(grpc_keepalive_trace)) {
       gpr_log(GPR_INFO, "%s: Ping delayed [%s]: already pinging",
-              t->is_client ? "CLIENT" : "SERVER", t->peer_string);
+              t->is_client ? "CLIENT" : "SERVER", t->peer_string.c_str());
     }
     return;
   }
@@ -69,7 +69,7 @@ static void maybe_initiate_ping(grpc_chttp2_transport* t) {
         GRPC_TRACE_FLAG_ENABLED(grpc_bdp_estimator_trace) ||
         GRPC_TRACE_FLAG_ENABLED(grpc_keepalive_trace)) {
       gpr_log(GPR_INFO, "%s: Ping delayed [%s]: too many recent pings: %d/%d",
-              t->is_client ? "CLIENT" : "SERVER", t->peer_string,
+              t->is_client ? "CLIENT" : "SERVER", t->peer_string.c_str(),
               t->ping_state.pings_before_data_required,
               t->ping_policy.max_pings_without_data);
     }
@@ -95,7 +95,7 @@ static void maybe_initiate_ping(grpc_chttp2_transport* t) {
       gpr_log(GPR_INFO,
               "%s: Ping delayed [%s]: not enough time elapsed since last ping. "
               " Last ping %f: Next ping %f: Now %f",
-              t->is_client ? "CLIENT" : "SERVER", t->peer_string,
+              t->is_client ? "CLIENT" : "SERVER", t->peer_string.c_str(),
               static_cast<double>(t->ping_state.last_ping_sent_time),
               static_cast<double>(next_allowed_ping), static_cast<double>(now));
     }
@@ -125,7 +125,7 @@ static void maybe_initiate_ping(grpc_chttp2_transport* t) {
       GRPC_TRACE_FLAG_ENABLED(grpc_bdp_estimator_trace) ||
       GRPC_TRACE_FLAG_ENABLED(grpc_keepalive_trace)) {
     gpr_log(GPR_INFO, "%s: Ping sent [%s]: %d/%d",
-            t->is_client ? "CLIENT" : "SERVER", t->peer_string,
+            t->is_client ? "CLIENT" : "SERVER", t->peer_string.c_str(),
             t->ping_state.pings_before_data_required,
             t->ping_policy.max_pings_without_data);
   }
@@ -165,7 +165,8 @@ static void report_stall(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
         "helpful data: [fc:pending=%" PRIdPTR ":pending-compressed=%" PRIdPTR
         ":flowed=%" PRId64 ":peer_initwin=%d:t_win=%" PRId64
         ":s_win=%d:s_delta=%" PRId64 "]",
-        t->peer_string, t, s->id, staller, s->flow_controlled_buffer.length,
+        t->peer_string.c_str(), t, s->id, staller,
+        s->flow_controlled_buffer.length,
         s->stream_compression_method ==
                 GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS
             ? 0
diff --git a/src/core/lib/iomgr/endpoint.cc b/src/core/lib/iomgr/endpoint.cc
index bb07fe79608..8954f9dc066 100644
--- a/src/core/lib/iomgr/endpoint.cc
+++ b/src/core/lib/iomgr/endpoint.cc
@@ -52,10 +52,14 @@ void grpc_endpoint_shutdown(grpc_endpoint* ep, grpc_error* why) {
 
 void grpc_endpoint_destroy(grpc_endpoint* ep) { ep->vtable->destroy(ep); }
 
-char* grpc_endpoint_get_peer(grpc_endpoint* ep) {
+absl::string_view grpc_endpoint_get_peer(grpc_endpoint* ep) {
   return ep->vtable->get_peer(ep);
 }
 
+absl::string_view grpc_endpoint_get_local_address(grpc_endpoint* ep) {
+  return ep->vtable->get_local_address(ep);
+}
+
 int grpc_endpoint_get_fd(grpc_endpoint* ep) { return ep->vtable->get_fd(ep); }
 
 grpc_resource_user* grpc_endpoint_get_resource_user(grpc_endpoint* ep) {
diff --git a/src/core/lib/iomgr/endpoint.h b/src/core/lib/iomgr/endpoint.h
index 932e7e15b9a..43c64f7ee2e 100644
--- a/src/core/lib/iomgr/endpoint.h
+++ b/src/core/lib/iomgr/endpoint.h
@@ -21,6 +21,8 @@
 
 #include <grpc/support/port_platform.h>
 
+#include "absl/strings/string_view.h"
+
 #include <grpc/slice.h>
 #include <grpc/slice_buffer.h>
 #include <grpc/support/time.h>
@@ -46,7 +48,8 @@ struct grpc_endpoint_vtable {
   void (*shutdown)(grpc_endpoint* ep, grpc_error* why);
   void (*destroy)(grpc_endpoint* ep);
   grpc_resource_user* (*get_resource_user)(grpc_endpoint* ep);
-  char* (*get_peer)(grpc_endpoint* ep);
+  absl::string_view (*get_peer)(grpc_endpoint* ep);
+  absl::string_view (*get_local_address)(grpc_endpoint* ep);
   int (*get_fd)(grpc_endpoint* ep);
   bool (*can_track_err)(grpc_endpoint* ep);
 };
@@ -59,7 +62,9 @@ struct grpc_endpoint_vtable {
 void grpc_endpoint_read(grpc_endpoint* ep, grpc_slice_buffer* slices,
                         grpc_closure* cb, bool urgent);
 
-char* grpc_endpoint_get_peer(grpc_endpoint* ep);
+absl::string_view grpc_endpoint_get_peer(grpc_endpoint* ep);
+
+absl::string_view grpc_endpoint_get_local_address(grpc_endpoint* ep);
 
 /* Get the file descriptor used by \a ep. Return -1 if \a ep is not using an fd.
  */
diff --git a/src/core/lib/iomgr/endpoint_cfstream.cc b/src/core/lib/iomgr/endpoint_cfstream.cc
index f9ee1c917bc..1751b4fcd43 100644
--- a/src/core/lib/iomgr/endpoint_cfstream.cc
+++ b/src/core/lib/iomgr/endpoint_cfstream.cc
@@ -34,6 +34,8 @@
 #include "src/core/lib/iomgr/closure.h"
 #include "src/core/lib/iomgr/endpoint.h"
 #include "src/core/lib/iomgr/error_cfstream.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
 #include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/slice/slice_string_helpers.h"
 
@@ -55,7 +57,8 @@ struct CFStreamEndpoint {
   grpc_closure read_action;
   grpc_closure write_action;
 
-  char* peer_string;
+  std::string peer_string;
+  std::string local_address;
   grpc_resource_user* resource_user;
   grpc_resource_user_slice_allocator slice_allocator;
 };
@@ -64,8 +67,7 @@ static void CFStreamFree(CFStreamEndpoint* ep) {
   CFRelease(ep->read_stream);
   CFRelease(ep->write_stream);
   CFSTREAM_HANDLE_UNREF(ep->stream_sync, "free");
-  gpr_free(ep->peer_string);
-  gpr_free(ep);
+  delete ep;
 }
 
 #ifndef NDEBUG
@@ -110,7 +112,7 @@ static grpc_error* CFStreamAnnotateError(grpc_error* src_error,
       grpc_error_set_int(src_error, GRPC_ERROR_INT_GRPC_STATUS,
                          GRPC_STATUS_UNAVAILABLE),
       GRPC_ERROR_STR_TARGET_ADDRESS,
-      grpc_slice_from_copied_string(ep->peer_string));
+      grpc_slice_from_copied_string(ep->peer_string.c_str()));
 }
 
 static void CallReadCb(CFStreamEndpoint* ep, grpc_error* error) {
@@ -124,7 +126,8 @@ static void CallReadCb(CFStreamEndpoint* ep, grpc_error* error) {
     for (i = 0; i < ep->read_slices->count; i++) {
       char* dump = grpc_dump_slice(ep->read_slices->slices[i],
                                    GPR_DUMP_HEX | GPR_DUMP_ASCII);
-      gpr_log(GPR_DEBUG, "READ %p (peer=%s): %s", ep, ep->peer_string, dump);
+      gpr_log(GPR_DEBUG, "READ %p (peer=%s): %s", ep, ep->peer_string.c_str(),
+              dump);
       gpr_free(dump);
     }
   }
@@ -230,7 +233,8 @@ static void WriteAction(void* arg, grpc_error* error) {
     if (grpc_tcp_trace.enabled()) {
       grpc_slice trace_slice = grpc_slice_sub(slice, 0, write_size);
       char* dump = grpc_dump_slice(trace_slice, GPR_DUMP_HEX | GPR_DUMP_ASCII);
-      gpr_log(GPR_DEBUG, "WRITE %p (peer=%s): %s", ep, ep->peer_string, dump);
+      gpr_log(GPR_DEBUG, "WRITE %p (peer=%s): %s", ep, ep->peer_string.c_str(),
+              dump);
       gpr_free(dump);
       grpc_slice_unref_internal(trace_slice);
     }
@@ -309,9 +313,14 @@ grpc_resource_user* CFStreamGetResourceUser(grpc_endpoint* ep) {
   return ep_impl->resource_user;
 }
 
-char* CFStreamGetPeer(grpc_endpoint* ep) {
+absl::string_view CFStreamGetPeer(grpc_endpoint* ep) {
   CFStreamEndpoint* ep_impl = reinterpret_cast<CFStreamEndpoint*>(ep);
-  return gpr_strdup(ep_impl->peer_string);
+  return ep_impl->peer_string;
+}
+
+absl::string_view CFStreamGetLocalAddress(grpc_endpoint* ep) {
+  CFStreamEndpoint* ep_impl = reinterpret_cast<CFStreamEndpoint*>(ep);
+  return ep_impl->local_address;
 }
 
 int CFStreamGetFD(grpc_endpoint* ep) { return 0; }
@@ -332,6 +341,7 @@ static const grpc_endpoint_vtable vtable = {CFStreamRead,
                                             CFStreamDestroy,
                                             CFStreamGetResourceUser,
                                             CFStreamGetPeer,
+                                            CFStreamGetLocalAddress,
                                             CFStreamGetFD,
                                             CFStreamCanTrackErr};
 
@@ -339,8 +349,7 @@ grpc_endpoint* grpc_cfstream_endpoint_create(
     CFReadStreamRef read_stream, CFWriteStreamRef write_stream,
     const char* peer_string, grpc_resource_quota* resource_quota,
     CFStreamHandle* stream_sync) {
-  CFStreamEndpoint* ep_impl =
-      static_cast<CFStreamEndpoint*>(gpr_malloc(sizeof(CFStreamEndpoint)));
+  CFStreamEndpoint* ep_impl = new CFStreamEndpoint;
   if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "CFStream endpoint:%p create readStream:%p writeStream: %p",
@@ -355,7 +364,19 @@ grpc_endpoint* grpc_cfstream_endpoint_create(
   ep_impl->stream_sync = stream_sync;
   CFSTREAM_HANDLE_REF(ep_impl->stream_sync, "endpoint create");
 
-  ep_impl->peer_string = gpr_strdup(peer_string);
+  ep_impl->peer_string = peer_string;
+  const int* native_handle =
+      reinterpret_cast<const int*>(CFReadStreamCopyProperty(
+          ep_impl->read_stream, kCFStreamPropertySocketNativeHandle));
+  grpc_resolved_address resolved_local_addr;
+  resolved_local_addr.len = sizeof(resolved_local_addr.addr);
+  if (getsockname(*native_handle,
+                  reinterpret_cast<sockaddr*>(resolved_local_addr.addr),
+                  &resolved_local_addr.len) < 0) {
+    ep_impl->local_address = "";
+  } else {
+    ep_impl->local_address = grpc_sockaddr_to_uri(&resolved_local_addr);
+  }
   ep_impl->read_cb = nil;
   ep_impl->write_cb = nil;
   ep_impl->read_slices = nil;
diff --git a/src/core/lib/iomgr/tcp_custom.cc b/src/core/lib/iomgr/tcp_custom.cc
index 6cf36f7d669..06763f69257 100644
--- a/src/core/lib/iomgr/tcp_custom.cc
+++ b/src/core/lib/iomgr/tcp_custom.cc
@@ -32,6 +32,7 @@
 #include "src/core/lib/iomgr/error.h"
 #include "src/core/lib/iomgr/iomgr_custom.h"
 #include "src/core/lib/iomgr/resource_quota.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
 #include "src/core/lib/iomgr/tcp_client.h"
 #include "src/core/lib/iomgr/tcp_custom.h"
 #include "src/core/lib/iomgr/tcp_server.h"
@@ -57,24 +58,24 @@ struct custom_tcp_endpoint {
   gpr_refcount refcount;
   grpc_custom_socket* socket;
 
-  grpc_closure* read_cb;
-  grpc_closure* write_cb;
+  grpc_closure* read_cb = nullptr;
+  grpc_closure* write_cb = nullptr;
 
-  grpc_slice_buffer* read_slices;
-  grpc_slice_buffer* write_slices;
+  grpc_slice_buffer* read_slices = nullptr;
+  grpc_slice_buffer* write_slices = nullptr;
 
   grpc_resource_user* resource_user;
   grpc_resource_user_slice_allocator slice_allocator;
 
   bool shutting_down;
 
-  char* peer_string;
+  std::string peer_string;
+  std::string local_address;
 };
 static void tcp_free(grpc_custom_socket* s) {
   custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)s->endpoint;
   grpc_resource_user_unref(tcp->resource_user);
-  gpr_free(tcp->peer_string);
-  gpr_free(tcp);
+  delete tcp;
   s->refs--;
   if (s->refs == 0) {
     grpc_custom_socket_vtable->destroy(s);
@@ -132,7 +133,8 @@ static void call_read_cb(custom_tcp_endpoint* tcp, grpc_error* error) {
     for (i = 0; i < tcp->read_slices->count; i++) {
       char* dump = grpc_dump_slice(tcp->read_slices->slices[i],
                                    GPR_DUMP_HEX | GPR_DUMP_ASCII);
-      gpr_log(GPR_INFO, "READ %p (peer=%s): %s", tcp, tcp->peer_string, dump);
+      gpr_log(GPR_INFO, "READ %p (peer=%s): %s", tcp, tcp->peer_string.c_str(),
+              dump);
       gpr_free(dump);
     }
   }
@@ -233,8 +235,8 @@ static void endpoint_write(grpc_endpoint* ep, grpc_slice_buffer* write_slices,
     for (j = 0; j < write_slices->count; j++) {
       char* data = grpc_dump_slice(write_slices->slices[j],
                                    GPR_DUMP_HEX | GPR_DUMP_ASCII);
-      gpr_log(GPR_INFO, "WRITE %p (peer=%s): %s", tcp->socket, tcp->peer_string,
-              data);
+      gpr_log(GPR_INFO, "WRITE %p (peer=%s): %s", tcp->socket,
+              tcp->peer_string.c_str(), data);
       gpr_free(data);
     }
   }
@@ -317,9 +319,14 @@ static void endpoint_destroy(grpc_endpoint* ep) {
   grpc_custom_socket_vtable->close(tcp->socket, custom_close_callback);
 }
 
-static char* endpoint_get_peer(grpc_endpoint* ep) {
+static absl::string_view endpoint_get_peer(grpc_endpoint* ep) {
   custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep;
-  return gpr_strdup(tcp->peer_string);
+  return tcp->peer_string;
+}
+
+static absl::string_view endpoint_get_local_address(grpc_endpoint* ep) {
+  custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep;
+  return tcp->local_address;
 }
 
 static grpc_resource_user* endpoint_get_resource_user(grpc_endpoint* ep) {
@@ -340,27 +347,36 @@ static grpc_endpoint_vtable vtable = {endpoint_read,
                                       endpoint_destroy,
                                       endpoint_get_resource_user,
                                       endpoint_get_peer,
+                                      endpoint_get_local_address,
                                       endpoint_get_fd,
                                       endpoint_can_track_err};
 
 grpc_endpoint* custom_tcp_endpoint_create(grpc_custom_socket* socket,
                                           grpc_resource_quota* resource_quota,
                                           const char* peer_string) {
-  custom_tcp_endpoint* tcp =
-      (custom_tcp_endpoint*)gpr_malloc(sizeof(custom_tcp_endpoint));
+  custom_tcp_endpoint* tcp = new custom_tcp_endpoint;
   grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
   grpc_core::ExecCtx exec_ctx;
 
   if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_log(GPR_INFO, "Creating TCP endpoint %p", socket);
   }
-  memset(tcp, 0, sizeof(custom_tcp_endpoint));
   socket->refs++;
   socket->endpoint = (grpc_endpoint*)tcp;
   tcp->socket = socket;
   tcp->base.vtable = &vtable;
   gpr_ref_init(&tcp->refcount, 1);
-  tcp->peer_string = gpr_strdup(peer_string);
+  tcp->peer_string = peer_string;
+  grpc_resolved_address resolved_local_addr;
+  resolved_local_addr.len = sizeof(resolved_local_addr.addr);
+  if (grpc_custom_socket_vtable->getsockname(
+          socket, reinterpret_cast<sockaddr*>(resolved_local_addr.addr),
+          reinterpret_cast<int*>(&resolved_local_addr.len)) !=
+      GRPC_ERROR_NONE) {
+    tcp->local_address = "";
+  } else {
+    tcp->local_address = grpc_sockaddr_to_uri(&resolved_local_addr);
+  }
   tcp->shutting_down = false;
   tcp->resource_user = grpc_resource_user_create(resource_quota, peer_string);
   grpc_resource_user_slice_allocator_init(
diff --git a/src/core/lib/iomgr/tcp_posix.cc b/src/core/lib/iomgr/tcp_posix.cc
index bd0562bfa74..3c5703460fc 100644
--- a/src/core/lib/iomgr/tcp_posix.cc
+++ b/src/core/lib/iomgr/tcp_posix.cc
@@ -54,6 +54,7 @@
 #include "src/core/lib/iomgr/buffer_list.h"
 #include "src/core/lib/iomgr/ev_posix.h"
 #include "src/core/lib/iomgr/executor.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
 #include "src/core/lib/iomgr/socket_utils_posix.h"
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/slice/slice_internal.h"
@@ -351,6 +352,8 @@ using grpc_core::TcpZerocopySendRecord;
 
 namespace {
 struct grpc_tcp {
+  grpc_tcp(int max_sends, size_t send_bytes_threshold)
+      : tcp_zerocopy_send_ctx(max_sends, send_bytes_threshold) {}
   grpc_endpoint base;
   grpc_fd* em_fd;
   int fd;
@@ -385,7 +388,8 @@ struct grpc_tcp {
   grpc_closure write_done_closure;
   grpc_closure error_closure;
 
-  char* peer_string;
+  std::string peer_string;
+  std::string local_address;
 
   grpc_resource_user* resource_user;
   grpc_resource_user_slice_allocator slice_allocator;
@@ -605,7 +609,7 @@ static grpc_error* tcp_annotate_error(grpc_error* src_error, grpc_tcp* tcp) {
            * choose to retry. */
           GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE),
       GRPC_ERROR_STR_TARGET_ADDRESS,
-      grpc_slice_from_copied_string(tcp->peer_string));
+      grpc_slice_from_copied_string(tcp->peer_string.c_str()));
 }
 
 static void tcp_handle_read(void* arg /* grpc_tcp */, grpc_error* error);
@@ -623,7 +627,6 @@ static void tcp_free(grpc_tcp* tcp) {
                  "tcp_unref_orphan");
   grpc_slice_buffer_destroy_internal(&tcp->last_read_buffer);
   grpc_resource_user_unref(tcp->resource_user);
-  gpr_free(tcp->peer_string);
   /* The lock is not really necessary here, since all refs have been released */
   gpr_mu_lock(&tcp->tb_mu);
   grpc_core::TracedBuffer::Shutdown(
@@ -632,8 +635,7 @@ static void tcp_free(grpc_tcp* tcp) {
   gpr_mu_unlock(&tcp->tb_mu);
   tcp->outgoing_buffer_arg = nullptr;
   gpr_mu_destroy(&tcp->tb_mu);
-  tcp->tcp_zerocopy_send_ctx.~TcpZerocopySendCtx();
-  gpr_free(tcp);
+  delete tcp;
 }
 
 #ifndef NDEBUG
@@ -680,7 +682,8 @@ static void call_read_cb(grpc_tcp* tcp, grpc_error* error) {
     gpr_log(GPR_INFO, "TCP:%p call_cb %p %p:%p", tcp, cb, cb->cb, cb->cb_arg);
     size_t i;
     const char* str = grpc_error_string(error);
-    gpr_log(GPR_INFO, "READ %p (peer=%s) error=%s", tcp, tcp->peer_string, str);
+    gpr_log(GPR_INFO, "READ %p (peer=%s) error=%s", tcp,
+            tcp->peer_string.c_str(), str);
 
     if (gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
       for (i = 0; i < tcp->incoming_buffer->count; i++) {
@@ -1563,7 +1566,7 @@ static void tcp_write(grpc_endpoint* ep, grpc_slice_buffer* buf,
     size_t i;
 
     for (i = 0; i < buf->count; i++) {
-      gpr_log(GPR_INFO, "WRITE %p (peer=%s)", tcp, tcp->peer_string);
+      gpr_log(GPR_INFO, "WRITE %p (peer=%s)", tcp, tcp->peer_string.c_str());
       if (gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
         char* data =
             grpc_dump_slice(buf->slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII);
@@ -1637,9 +1640,14 @@ static void tcp_delete_from_pollset_set(grpc_endpoint* ep,
   grpc_pollset_set_del_fd(pollset_set, tcp->em_fd);
 }
 
-static char* tcp_get_peer(grpc_endpoint* ep) {
+static absl::string_view tcp_get_peer(grpc_endpoint* ep) {
   grpc_tcp* tcp = reinterpret_cast<grpc_tcp*>(ep);
-  return gpr_strdup(tcp->peer_string);
+  return tcp->peer_string;
+}
+
+static absl::string_view tcp_get_local_address(grpc_endpoint* ep) {
+  grpc_tcp* tcp = reinterpret_cast<grpc_tcp*>(ep);
+  return tcp->local_address;
 }
 
 static int tcp_get_fd(grpc_endpoint* ep) {
@@ -1677,6 +1685,7 @@ static const grpc_endpoint_vtable vtable = {tcp_read,
                                             tcp_destroy,
                                             tcp_get_resource_user,
                                             tcp_get_peer,
+                                            tcp_get_local_address,
                                             tcp_get_fd,
                                             tcp_can_track_err};
 
@@ -1745,10 +1754,21 @@ grpc_endpoint* grpc_tcp_create(grpc_fd* em_fd,
   tcp_read_chunk_size = GPR_CLAMP(tcp_read_chunk_size, tcp_min_read_chunk_size,
                                   tcp_max_read_chunk_size);
 
-  grpc_tcp* tcp = static_cast<grpc_tcp*>(gpr_malloc(sizeof(grpc_tcp)));
+  grpc_tcp* tcp = new grpc_tcp(tcp_tx_zerocopy_max_simult_sends,
+                               tcp_tx_zerocopy_send_bytes_thresh);
   tcp->base.vtable = &vtable;
-  tcp->peer_string = gpr_strdup(peer_string);
+  tcp->peer_string = peer_string;
   tcp->fd = grpc_fd_wrapped_fd(em_fd);
+  grpc_resolved_address resolved_local_addr;
+  memset(&resolved_local_addr, 0, sizeof(resolved_local_addr));
+  resolved_local_addr.len = sizeof(resolved_local_addr.addr);
+  if (getsockname(tcp->fd,
+                  reinterpret_cast<sockaddr*>(resolved_local_addr.addr),
+                  &resolved_local_addr.len) < 0) {
+    tcp->local_address = "";
+  } else {
+    tcp->local_address = grpc_sockaddr_to_uri(&resolved_local_addr);
+  }
   tcp->read_cb = nullptr;
   tcp->write_cb = nullptr;
   tcp->current_zerocopy_send = nullptr;
@@ -1765,8 +1785,6 @@ grpc_endpoint* grpc_tcp_create(grpc_fd* em_fd,
   tcp->socket_ts_enabled = false;
   tcp->ts_capable = true;
   tcp->outgoing_buffer_arg = nullptr;
-  new (&tcp->tcp_zerocopy_send_ctx) TcpZerocopySendCtx(
-      tcp_tx_zerocopy_max_simult_sends, tcp_tx_zerocopy_send_bytes_thresh);
   if (tcp_tx_zerocopy_enabled && !tcp->tcp_zerocopy_send_ctx.memory_limited()) {
 #ifdef GRPC_LINUX_ERRQUEUE
     const int enable = 1;
diff --git a/src/core/lib/iomgr/tcp_windows.cc b/src/core/lib/iomgr/tcp_windows.cc
index ab5a2a21e15..d28a54ccf43 100644
--- a/src/core/lib/iomgr/tcp_windows.cc
+++ b/src/core/lib/iomgr/tcp_windows.cc
@@ -125,17 +125,17 @@ typedef struct grpc_tcp {
   int shutting_down;
   grpc_error* shutdown_error;
 
-  char* peer_string;
+  std::string peer_string;
+  std::string local_address;
 } grpc_tcp;
 
 static void tcp_free(grpc_tcp* tcp) {
   grpc_winsocket_destroy(tcp->socket);
   gpr_mu_destroy(&tcp->mu);
-  gpr_free(tcp->peer_string);
   grpc_slice_buffer_destroy_internal(&tcp->last_read_buffer);
   grpc_resource_user_unref(tcp->resource_user);
   if (tcp->shutting_down) GRPC_ERROR_UNREF(tcp->shutdown_error);
-  gpr_free(tcp);
+  delete tcp;
 }
 
 #ifndef NDEBUG
@@ -213,8 +213,8 @@ static void on_read(void* tcpp, grpc_error* error) {
           for (i = 0; i < tcp->read_slices->count; i++) {
             char* dump = grpc_dump_slice(tcp->read_slices->slices[i],
                                          GPR_DUMP_HEX | GPR_DUMP_ASCII);
-            gpr_log(GPR_INFO, "READ %p (peer=%s): %s", tcp, tcp->peer_string,
-                    dump);
+            gpr_log(GPR_INFO, "READ %p (peer=%s): %s", tcp,
+                    tcp->peer_string.c_str(), dump);
             gpr_free(dump);
           }
         }
@@ -361,7 +361,8 @@ static void win_write(grpc_endpoint* ep, grpc_slice_buffer* slices,
     for (i = 0; i < slices->count; i++) {
       char* data =
           grpc_dump_slice(slices->slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII);
-      gpr_log(GPR_INFO, "WRITE %p (peer=%s): %s", tcp, tcp->peer_string, data);
+      gpr_log(GPR_INFO, "WRITE %p (peer=%s): %s", tcp, tcp->peer_string.c_str(),
+              data);
       gpr_free(data);
     }
   }
@@ -475,9 +476,14 @@ static void win_destroy(grpc_endpoint* ep) {
   TCP_UNREF(tcp, "destroy");
 }
 
-static char* win_get_peer(grpc_endpoint* ep) {
+static absl::string_view win_get_peer(grpc_endpoint* ep) {
   grpc_tcp* tcp = (grpc_tcp*)ep;
-  return gpr_strdup(tcp->peer_string);
+  return tcp->peer_string;
+}
+
+static absl::string_view win_get_local_address(grpc_endpoint* ep) {
+  grpc_tcp* tcp = (grpc_tcp*)ep;
+  return tcp->local_address;
 }
 
 static grpc_resource_user* win_get_resource_user(grpc_endpoint* ep) {
@@ -498,6 +504,7 @@ static grpc_endpoint_vtable vtable = {win_read,
                                       win_destroy,
                                       win_get_resource_user,
                                       win_get_peer,
+                                      win_get_local_address,
                                       win_get_fd,
                                       win_can_track_err};
 
@@ -514,7 +521,7 @@ grpc_endpoint* grpc_tcp_create(grpc_winsocket* socket,
       }
     }
   }
-  grpc_tcp* tcp = (grpc_tcp*)gpr_malloc(sizeof(grpc_tcp));
+  grpc_tcp* tcp = new grpc_tcp;
   memset(tcp, 0, sizeof(grpc_tcp));
   tcp->base.vtable = &vtable;
   tcp->socket = socket;
@@ -522,7 +529,16 @@ grpc_endpoint* grpc_tcp_create(grpc_winsocket* socket,
   gpr_ref_init(&tcp->refcount, 1);
   GRPC_CLOSURE_INIT(&tcp->on_read, on_read, tcp, grpc_schedule_on_exec_ctx);
   GRPC_CLOSURE_INIT(&tcp->on_write, on_write, tcp, grpc_schedule_on_exec_ctx);
-  tcp->peer_string = gpr_strdup(peer_string);
+  grpc_resolved_address resolved_local_addr;
+  resolved_local_addr.len = sizeof(resolved_local_addr.addr);
+  if (getsockname(tcp->socket->socket,
+                  reinterpret_cast<sockaddr*>(resolved_local_addr.addr),
+                  &resolved_local_addr.len) < 0) {
+    tcp->local_address = "";
+  } else {
+    tcp->local_address = grpc_sockaddr_to_uri(&resolved_local_addr);
+  }
+  tcp->peer_string = peer_string;
   grpc_slice_buffer_init(&tcp->last_read_buffer);
   tcp->resource_user = grpc_resource_user_create(resource_quota, peer_string);
   grpc_resource_quota_unref_internal(resource_quota);
diff --git a/src/core/lib/security/transport/secure_endpoint.cc b/src/core/lib/security/transport/secure_endpoint.cc
index eaa8c7da704..f42aaeae36b 100644
--- a/src/core/lib/security/transport/secure_endpoint.cc
+++ b/src/core/lib/security/transport/secure_endpoint.cc
@@ -401,11 +401,16 @@ static void endpoint_delete_from_pollset_set(grpc_endpoint* secure_ep,
   grpc_endpoint_delete_from_pollset_set(ep->wrapped_ep, pollset_set);
 }
 
-static char* endpoint_get_peer(grpc_endpoint* secure_ep) {
+static absl::string_view endpoint_get_peer(grpc_endpoint* secure_ep) {
   secure_endpoint* ep = reinterpret_cast<secure_endpoint*>(secure_ep);
   return grpc_endpoint_get_peer(ep->wrapped_ep);
 }
 
+static absl::string_view endpoint_get_local_address(grpc_endpoint* secure_ep) {
+  secure_endpoint* ep = reinterpret_cast<secure_endpoint*>(secure_ep);
+  return grpc_endpoint_get_local_address(ep->wrapped_ep);
+}
+
 static int endpoint_get_fd(grpc_endpoint* secure_ep) {
   secure_endpoint* ep = reinterpret_cast<secure_endpoint*>(secure_ep);
   return grpc_endpoint_get_fd(ep->wrapped_ep);
@@ -431,6 +436,7 @@ static const grpc_endpoint_vtable vtable = {endpoint_read,
                                             endpoint_destroy,
                                             endpoint_get_resource_user,
                                             endpoint_get_peer,
+                                            endpoint_get_local_address,
                                             endpoint_get_fd,
                                             endpoint_can_track_err};
 
diff --git a/test/core/util/mock_endpoint.cc b/test/core/util/mock_endpoint.cc
index b001fd764a7..7e495c311cc 100644
--- a/test/core/util/mock_endpoint.cc
+++ b/test/core/util/mock_endpoint.cc
@@ -98,8 +98,12 @@ static void me_destroy(grpc_endpoint* ep) {
   gpr_free(m);
 }
 
-static char* me_get_peer(grpc_endpoint* /*ep*/) {
-  return gpr_strdup("fake:mock_endpoint");
+static absl::string_view me_get_peer(grpc_endpoint* /*ep*/) {
+  return "fake:mock_endpoint";
+}
+
+static absl::string_view me_get_local_address(grpc_endpoint* /*ep*/) {
+  return "fake:mock_endpoint";
 }
 
 static grpc_resource_user* me_get_resource_user(grpc_endpoint* ep) {
@@ -120,6 +124,7 @@ static const grpc_endpoint_vtable vtable = {me_read,
                                             me_destroy,
                                             me_get_resource_user,
                                             me_get_peer,
+                                            me_get_local_address,
                                             me_get_fd,
                                             me_can_track_err};
 
diff --git a/test/core/util/passthru_endpoint.cc b/test/core/util/passthru_endpoint.cc
index 0b14c55c73e..59e8276fe1f 100644
--- a/test/core/util/passthru_endpoint.cc
+++ b/test/core/util/passthru_endpoint.cc
@@ -152,11 +152,18 @@ static void me_destroy(grpc_endpoint* ep) {
   }
 }
 
-static char* me_get_peer(grpc_endpoint* ep) {
+static absl::string_view me_get_peer(grpc_endpoint* ep) {
   passthru_endpoint* p = (reinterpret_cast<half*>(ep))->parent;
   return (reinterpret_cast<half*>(ep)) == &p->client
-             ? gpr_strdup("fake:mock_client_endpoint")
-             : gpr_strdup("fake:mock_server_endpoint");
+             ? "fake:mock_client_endpoint"
+             : "fake:mock_server_endpoint";
+}
+
+static absl::string_view me_get_local_address(grpc_endpoint* ep) {
+  passthru_endpoint* p = (reinterpret_cast<half*>(ep))->parent;
+  return (reinterpret_cast<half*>(ep)) == &p->client
+             ? "fake:mock_client_endpoint"
+             : "fake:mock_server_endpoint";
 }
 
 static int me_get_fd(grpc_endpoint* /*ep*/) { return -1; }
@@ -178,6 +185,7 @@ static const grpc_endpoint_vtable vtable = {
     me_destroy,
     me_get_resource_user,
     me_get_peer,
+    me_get_local_address,
     me_get_fd,
     me_can_track_err,
 };
diff --git a/test/core/util/reconnect_server.cc b/test/core/util/reconnect_server.cc
index 80e4ad8060f..626fe95ced3 100644
--- a/test/core/util/reconnect_server.cc
+++ b/test/core/util/reconnect_server.cc
@@ -21,10 +21,12 @@
 #include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
 #include <grpc/support/sync.h>
 #include <grpc/support/time.h>
 #include <string.h>
 
+#include "absl/strings/string_view.h"
 #include "src/core/lib/iomgr/endpoint.h"
 #include "src/core/lib/iomgr/sockaddr.h"
 #include "src/core/lib/iomgr/tcp_server.h"
@@ -59,8 +61,8 @@ static void on_connect(void* arg, grpc_endpoint* tcp,
                        grpc_pollset* /*accepting_pollset*/,
                        grpc_tcp_server_acceptor* acceptor) {
   gpr_free(acceptor);
-  char* peer;
-  char* last_colon;
+  absl::string_view peer;
+  int last_colon;
   reconnect_server* server = static_cast<reconnect_server*>(arg);
   gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
   timestamp_list* new_tail;
@@ -68,18 +70,16 @@ static void on_connect(void* arg, grpc_endpoint* tcp,
   grpc_endpoint_shutdown(tcp,
                          GRPC_ERROR_CREATE_FROM_STATIC_STRING("Connected"));
   grpc_endpoint_destroy(tcp);
-  if (peer) {
-    last_colon = strrchr(peer, ':');
-    if (server->peer == nullptr) {
-      server->peer = peer;
-    } else {
-      if (last_colon == nullptr) {
-        gpr_log(GPR_ERROR, "peer does not contain a ':'");
-      } else if (strncmp(server->peer, peer,
-                         static_cast<size_t>(last_colon - peer)) != 0) {
-        gpr_log(GPR_ERROR, "mismatched peer! %s vs %s", server->peer, peer);
-      }
-      gpr_free(peer);
+  last_colon = peer.rfind(':');
+  if (server->peer == nullptr) {
+    server->peer = new std::string(peer);
+  } else {
+    if (last_colon == std::string::npos) {
+      gpr_log(GPR_ERROR, "peer does not contain a ':'");
+    } else if (peer.compare(0, static_cast<size_t>(last_colon),
+                            *server->peer) != 0) {
+      gpr_log(GPR_ERROR, "mismatched peer! %s vs %s", server->peer->c_str(),
+              std::string(peer).c_str());
     }
   }
   new_tail = static_cast<timestamp_list*>(gpr_malloc(sizeof(timestamp_list)));
@@ -119,7 +119,7 @@ void reconnect_server_clear_timestamps(reconnect_server* server) {
     server->head = new_head;
   }
   server->tail = nullptr;
-  gpr_free(server->peer);
+  delete server->peer;
   server->peer = nullptr;
 }
 
diff --git a/test/core/util/reconnect_server.h b/test/core/util/reconnect_server.h
index d15abe529df..f7264dd156f 100644
--- a/test/core/util/reconnect_server.h
+++ b/test/core/util/reconnect_server.h
@@ -32,7 +32,7 @@ typedef struct reconnect_server {
   test_tcp_server tcp_server;
   timestamp_list* head;
   timestamp_list* tail;
-  char* peer;
+  std::string* peer;
   int max_reconnect_backoff_ms;
 } reconnect_server;
 
diff --git a/test/core/util/trickle_endpoint.cc b/test/core/util/trickle_endpoint.cc
index f313d05f593..603b51ab80c 100644
--- a/test/core/util/trickle_endpoint.cc
+++ b/test/core/util/trickle_endpoint.cc
@@ -122,11 +122,16 @@ static grpc_resource_user* te_get_resource_user(grpc_endpoint* ep) {
   return grpc_endpoint_get_resource_user(te->wrapped);
 }
 
-static char* te_get_peer(grpc_endpoint* ep) {
+static absl::string_view te_get_peer(grpc_endpoint* ep) {
   trickle_endpoint* te = reinterpret_cast<trickle_endpoint*>(ep);
   return grpc_endpoint_get_peer(te->wrapped);
 }
 
+static absl::string_view te_get_local_address(grpc_endpoint* ep) {
+  trickle_endpoint* te = reinterpret_cast<trickle_endpoint*>(ep);
+  return grpc_endpoint_get_local_address(te->wrapped);
+}
+
 static int te_get_fd(grpc_endpoint* ep) {
   trickle_endpoint* te = reinterpret_cast<trickle_endpoint*>(ep);
   return grpc_endpoint_get_fd(te->wrapped);
@@ -151,6 +156,7 @@ static const grpc_endpoint_vtable vtable = {te_read,
                                             te_destroy,
                                             te_get_resource_user,
                                             te_get_peer,
+                                            te_get_local_address,
                                             te_get_fd,
                                             te_can_track_err};
 
diff --git a/test/cpp/end2end/port_sharing_end2end_test.cc b/test/cpp/end2end/port_sharing_end2end_test.cc
index 94b4540cf70..f342bd00a85 100644
--- a/test/cpp/end2end/port_sharing_end2end_test.cc
+++ b/test/cpp/end2end/port_sharing_end2end_test.cc
@@ -156,9 +156,8 @@ class TestTcpServer {
  private:
   void OnConnect(grpc_endpoint* tcp, grpc_pollset* /*accepting_pollset*/,
                  grpc_tcp_server_acceptor* acceptor) {
-    char* peer = grpc_endpoint_get_peer(tcp);
-    gpr_log(GPR_INFO, "Got incoming connection! from %s", peer);
-    gpr_free(peer);
+    std::string peer(grpc_endpoint_get_peer(tcp));
+    gpr_log(GPR_INFO, "Got incoming connection! from %s", peer.c_str());
     EXPECT_FALSE(acceptor->external_connection);
     listener_fd_ = grpc_tcp_server_port_fd(
         acceptor->from_server, acceptor->port_index, acceptor->fd_index);
diff --git a/test/cpp/microbenchmarks/bm_chttp2_transport.cc b/test/cpp/microbenchmarks/bm_chttp2_transport.cc
index cdb313b8e3a..6a783d30aca 100644
--- a/test/cpp/microbenchmarks/bm_chttp2_transport.cc
+++ b/test/cpp/microbenchmarks/bm_chttp2_transport.cc
@@ -53,6 +53,7 @@ class DummyEndpoint : public grpc_endpoint {
                                                    destroy,
                                                    get_resource_user,
                                                    get_peer,
+                                                   get_local_address,
                                                    get_fd,
                                                    can_track_err};
     grpc_endpoint::vtable = &my_vtable;
@@ -124,7 +125,10 @@ class DummyEndpoint : public grpc_endpoint {
   static grpc_resource_user* get_resource_user(grpc_endpoint* ep) {
     return static_cast<DummyEndpoint*>(ep)->ru_;
   }
-  static char* get_peer(grpc_endpoint* /*ep*/) { return gpr_strdup("test"); }
+  static absl::string_view get_peer(grpc_endpoint* /*ep*/) { return "test"; }
+  static absl::string_view get_local_address(grpc_endpoint* /*ep*/) {
+    return "test";
+  }
   static int get_fd(grpc_endpoint* /*ep*/) { return 0; }
   static bool can_track_err(grpc_endpoint* /*ep*/) { return false; }
 };

From 6f40be0d8660b92c497fbf87b4c652eaf19bc177 Mon Sep 17 00:00:00 2001
From: Eric Gribkoff <ericgribkoff@google.com>
Date: Wed, 29 Jul 2020 14:13:34 -0700
Subject: [PATCH 10/38] Add grpc-java v1.31.0 to interop test client matrix

---
 tools/interop_matrix/client_matrix.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tools/interop_matrix/client_matrix.py b/tools/interop_matrix/client_matrix.py
index 891e58f0d68..655b8fa94e0 100644
--- a/tools/interop_matrix/client_matrix.py
+++ b/tools/interop_matrix/client_matrix.py
@@ -221,6 +221,7 @@ LANG_RELEASE_MATRIX = {
             ('v1.28.1', ReleaseInfo()),
             ('v1.29.0', ReleaseInfo()),
             ('v1.30.2', ReleaseInfo()),
+            ('v1.31.0', ReleaseInfo()),
         ]),
     'python':
         OrderedDict([

From f22d3dc390003aaf76d07580c23bc74199fc6650 Mon Sep 17 00:00:00 2001
From: srini100 <psrini@google.com>
Date: Thu, 30 Jul 2020 12:25:27 -0700
Subject: [PATCH 11/38] Add supported features in v1.31.0

---
 doc/grpc_xds_features.md | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/doc/grpc_xds_features.md b/doc/grpc_xds_features.md
index 3537db2b965..798a072e7ce 100644
--- a/doc/grpc_xds_features.md
+++ b/doc/grpc_xds_features.md
@@ -36,5 +36,6 @@ to authenticate with the xDS server.
 
 Features | gRFCs  | [C++, Python,<br> Ruby, PHP, C#](https://github.com/grpc/grpc/releases) | [Java](https://github.com/grpc/grpc-java/releases) | [Go](https://github.com/grpc/grpc-go/releases)
 ---------|--------|--------------|------|------
-**xDS Infrastructure in gRPC client channel:**<br>LDS->RDS->CDS->EDS flow,<br>ADS stream, | [A27](https://github.com/grpc/proposal/blob/master/A27-xds-global-load-balancing.md) | v1.30.0  | v1.30.0 | v1.30.0 |
-**Load Balancing:**<br>Virtual host matching,<br>Only default path ("" or "/") matching,<br>Priority-based weighted round-robin locality picking,<br>Round-robin endpoint picking within locality,<br>Cluster route action,<br>Client-side Load reporting via [LRS](https://github.com/envoyproxy/data-plane-api/blob/master/envoy/service/load_stats/v2/lrs.proto)| [A27](https://github.com/grpc/proposal/blob/master/A27-xds-global-load-balancing.md) | v1.30.0  | v1.30.0 | v1.30.0 |
+**xDS Infrastructure in gRPC client channel:**<ul><li>LDS->RDS->CDS->EDS flow</li><li>ADS stream</li></ul> | [A27](https://github.com/grpc/proposal/blob/master/A27-xds-global-load-balancing.md) | v1.30.0  | v1.30.0 | v1.30.0 |
+**Load Balancing:**<ul><li>[Virtual host](https://www.envoyproxy.io/docs/envoy/latest/api-v2/api/v2/route/route_components.proto#route-virtualhost) domains matching</li><li>Only default path ("" or "/") matching</li><li>Priority-based weighted round-robin locality picking</li><li>Round-robin endpoint picking within locality</li><li>[Cluster](https://www.envoyproxy.io/docs/envoy/latest/api-v2/api/v2/route/route_components.proto#envoy-api-msg-route-routeaction) route action</li><li>Client-side Load reporting via [LRS](https://github.com/envoyproxy/data-plane-api/blob/master/envoy/service/load_stats/v2/lrs.proto)</li></ul> | [A27](https://github.com/grpc/proposal/blob/master/A27-xds-global-load-balancing.md) | v1.30.0  | v1.30.0 | v1.30.0 |
+Request matching based on:<ul><li>[Path](https://www.envoyproxy.io/docs/envoy/latest/api-v2/api/v2/route/route_components.proto#route-routematch) (prefix, full path and safe regex)</li><li>[Headers](https://www.envoyproxy.io/docs/envoy/latest/api-v2/api/v2/route/route_components.proto#route-headermatcher)</li></ul>Request routing to multiple clusters based on [weights](https://www.envoyproxy.io/docs/envoy/latest/api-v2/api/v2/route/route_components.proto#route-weightedcluster) | [A28](https://github.com/grpc/proposal/blob/master/A28-xds-traffic-splitting-and-routing.md) | v1.31.0 | v1.31.0 | v1.31.0 |

From 555d34ea7ce42be5b5ab59f13cd76f6fbfa92807 Mon Sep 17 00:00:00 2001
From: Karthik Ravi Shankar <karthikrs@google.com>
Date: Thu, 30 Jul 2020 12:26:36 -0700
Subject: [PATCH 12/38] Make fixes in server_builder_impl.h

---
 include/grpcpp/server_builder_impl.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/include/grpcpp/server_builder_impl.h b/include/grpcpp/server_builder_impl.h
index c86aa8eef4c..7cae54bc853 100644
--- a/include/grpcpp/server_builder_impl.h
+++ b/include/grpcpp/server_builder_impl.h
@@ -43,13 +43,13 @@ namespace grpc_impl {
 class CompletionQueue;
 class Server;
 class ServerCompletionQueue;
-class ServerCredentials;
 }  // namespace grpc_impl
 
 namespace grpc {
 
 class AsyncGenericService;
 class ResourceQuota;
+class ServerCredentials;
 class Service;
 namespace testing {
 class ServerBuilderPluginTest;
@@ -134,7 +134,7 @@ class ServerBuilder {
   /// does not modify this pointer.
   ServerBuilder& AddListeningPort(
       const std::string& addr_uri,
-      std::shared_ptr<grpc_impl::ServerCredentials> creds,
+      std::shared_ptr<grpc::ServerCredentials> creds,
       int* selected_port = nullptr);
 
   /// Add a completion queue for handling asynchronous services.
@@ -317,7 +317,7 @@ class ServerBuilder {
   /// Experimental, to be deprecated
   struct Port {
     std::string addr;
-    std::shared_ptr<grpc_impl::ServerCredentials> creds;
+    std::shared_ptr<grpc::ServerCredentials> creds;
     int* selected_port;
   };
 
@@ -385,7 +385,7 @@ class ServerBuilder {
   /// List of completion queues added via \a AddCompletionQueue method.
   std::vector<grpc_impl::ServerCompletionQueue*> cqs_;
 
-  std::shared_ptr<grpc_impl::ServerCredentials> creds_;
+  std::shared_ptr<grpc::ServerCredentials> creds_;
   std::vector<std::unique_ptr<grpc::ServerBuilderPlugin>> plugins_;
   grpc_resource_quota* resource_quota_;
   grpc::AsyncGenericService* generic_service_{nullptr};

From 218c18d15da5dd31a15d025b7a08e59913649612 Mon Sep 17 00:00:00 2001
From: Esun Kim <veblush@google.com>
Date: Thu, 30 Jul 2020 13:43:55 -0700
Subject: [PATCH 13/38] Disable GPR_ABSEIL_SYNC on Apple platforms

---
 include/grpc/impl/codegen/port_platform.h | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/include/grpc/impl/codegen/port_platform.h b/include/grpc/impl/codegen/port_platform.h
index a3d3bb27b5a..91e3e39b0b4 100644
--- a/include/grpc/impl/codegen/port_platform.h
+++ b/include/grpc/impl/codegen/port_platform.h
@@ -31,8 +31,14 @@
  * Defines GPR_ABSEIL_SYNC to use synchronization features from Abseil
  */
 #ifndef GPR_ABSEIL_SYNC
+#if defined(__APPLE__)
+// This is disabled on Apple platforms because macos/grpc_basictests_c_cpp
+// fails with this. https://github.com/grpc/grpc/issues/23661
+#define GPR_ABSEIL_SYNC 0
+#else
 #define GPR_ABSEIL_SYNC 1
 #endif
+#endif  // GPR_ABSEIL_SYNC
 
 /* Get windows.h included everywhere (we need it) */
 #if defined(_WIN64) || defined(WIN64) || defined(_WIN32) || defined(WIN32)

From 9494b834b14742a616715ca1bb4cbd7b80e9768d Mon Sep 17 00:00:00 2001
From: Esun Kim <veblush@google.com>
Date: Thu, 30 Jul 2020 13:58:01 -0700
Subject: [PATCH 14/38] Add TestEnvironment to tests

---
 test/cpp/util/byte_buffer_test.cc   | 3 +++
 test/cpp/util/error_details_test.cc | 2 ++
 test/cpp/util/string_ref_test.cc    | 3 +++
 test/cpp/util/time_test.cc          | 3 +++
 4 files changed, 11 insertions(+)

diff --git a/test/cpp/util/byte_buffer_test.cc b/test/cpp/util/byte_buffer_test.cc
index fdae56a90c2..c63f351a8f0 100644
--- a/test/cpp/util/byte_buffer_test.cc
+++ b/test/cpp/util/byte_buffer_test.cc
@@ -27,6 +27,8 @@
 #include <grpcpp/support/slice.h>
 #include <gtest/gtest.h>
 
+#include "test/core/util/test_config.h"
+
 namespace grpc {
 
 static internal::GrpcLibraryInitializer g_gli_initializer;
@@ -125,6 +127,7 @@ TEST_F(ByteBufferTest, SerializationMakesCopy) {
 }  // namespace grpc
 
 int main(int argc, char** argv) {
+  grpc::testing::TestEnvironment env(argc, argv);
   ::testing::InitGoogleTest(&argc, argv);
   int ret = RUN_ALL_TESTS();
   return ret;
diff --git a/test/cpp/util/error_details_test.cc b/test/cpp/util/error_details_test.cc
index d88005267f8..438f75f4cd4 100644
--- a/test/cpp/util/error_details_test.cc
+++ b/test/cpp/util/error_details_test.cc
@@ -21,6 +21,7 @@
 
 #include "src/proto/grpc/status/status.pb.h"
 #include "src/proto/grpc/testing/echo_messages.pb.h"
+#include "test/core/util/test_config.h"
 
 namespace grpc {
 namespace {
@@ -118,6 +119,7 @@ TEST(SetTest, ValidScopeErrorCode) {
 }  // namespace grpc
 
 int main(int argc, char** argv) {
+  grpc::testing::TestEnvironment env(argc, argv);
   ::testing::InitGoogleTest(&argc, argv);
   return RUN_ALL_TESTS();
 }
diff --git a/test/cpp/util/string_ref_test.cc b/test/cpp/util/string_ref_test.cc
index 031ec33241b..8e3259b764b 100644
--- a/test/cpp/util/string_ref_test.cc
+++ b/test/cpp/util/string_ref_test.cc
@@ -22,6 +22,8 @@
 
 #include <gtest/gtest.h>
 
+#include "test/core/util/test_config.h"
+
 namespace grpc {
 namespace {
 
@@ -197,6 +199,7 @@ TEST_F(StringRefTest, ComparisonOperators) {
 }  // namespace grpc
 
 int main(int argc, char** argv) {
+  grpc::testing::TestEnvironment env(argc, argv);
   ::testing::InitGoogleTest(&argc, argv);
   return RUN_ALL_TESTS();
 }
diff --git a/test/cpp/util/time_test.cc b/test/cpp/util/time_test.cc
index a8851a583d2..bcbfa14f94f 100644
--- a/test/cpp/util/time_test.cc
+++ b/test/cpp/util/time_test.cc
@@ -20,6 +20,8 @@
 #include <grpcpp/support/time.h>
 #include <gtest/gtest.h>
 
+#include "test/core/util/test_config.h"
+
 using std::chrono::duration_cast;
 using std::chrono::microseconds;
 using std::chrono::system_clock;
@@ -64,6 +66,7 @@ TEST_F(TimeTest, InfFuture) {
 }  // namespace grpc
 
 int main(int argc, char** argv) {
+  grpc::testing::TestEnvironment env(argc, argv);
   ::testing::InitGoogleTest(&argc, argv);
   return RUN_ALL_TESTS();
 }

From 3cdd6ccb26c288644b667076181a2368b80e43b0 Mon Sep 17 00:00:00 2001
From: Esun Kim <veblush@google.com>
Date: Thu, 30 Jul 2020 19:30:56 -0700
Subject: [PATCH 15/38] Add MSAN workaround to TestEnvironment

---
 test/core/util/test_config.cc | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/test/core/util/test_config.cc b/test/core/util/test_config.cc
index b926b57280a..391467ba789 100644
--- a/test/core/util/test_config.cc
+++ b/test/core/util/test_config.cc
@@ -413,6 +413,15 @@ TestEnvironment::~TestEnvironment() {
       break;
     }
   }
+  if (BuiltUnderMsan()) {
+    // This is a workaround for MSAN. MSAN doesn't like having shutdown thread
+    // running. Although the code above waits until shutdown is done, chances
+    // are that thread itself is still alive. To workaround this problem, this
+    // is going to wait for 0.5 sec to give a chance to the shutdown thread to
+    // exit. https://github.com/grpc/grpc/issues/23695
+    gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
+                                 gpr_time_from_millis(500, GPR_TIMESPAN)));
+  }
   gpr_log(GPR_INFO, "TestEnvironment ends");
 }
 

From 307e175ab24a7b3e4953cb69b61ee04ed659669e Mon Sep 17 00:00:00 2001
From: Alexander Polcyn <apolcyn@google.com>
Date: Thu, 30 Jul 2020 16:52:24 -0700
Subject: [PATCH 16/38] Make fake insecure servers in ALTS concurrent
 connectivity test eagerly send an empty settings frame upon receiving new
 connections

---
 .../alts_concurrent_connectivity_test.cc      | 98 +++++++++++++++----
 1 file changed, 81 insertions(+), 17 deletions(-)

diff --git a/test/core/tsi/alts/handshaker/alts_concurrent_connectivity_test.cc b/test/core/tsi/alts/handshaker/alts_concurrent_connectivity_test.cc
index 4ec5a612d76..5e351398fed 100644
--- a/test/core/tsi/alts/handshaker/alts_concurrent_connectivity_test.cc
+++ b/test/core/tsi/alts/handshaker/alts_concurrent_connectivity_test.cc
@@ -334,9 +334,17 @@ class FakeTcpServer {
     CLOSE_SOCKET,
   };
 
-  FakeTcpServer(
+  enum class AcceptMode {
+    kWaitForClientToSendFirstBytes,  // useful for emulating ALTS based
+                                     // grpc servers
+    kEagerlySendSettings,  // useful for emulating insecure grpc servers (e.g.
+                           // ALTS handshake servers)
+  };
+
+  explicit FakeTcpServer(
+      AcceptMode accept_mode,
       const std::function<ProcessReadResult(int, int, int)>& process_read_cb)
-      : process_read_cb_(process_read_cb) {
+      : accept_mode_(accept_mode), process_read_cb_(process_read_cb) {
     port_ = grpc_pick_unused_port_or_die();
     accept_socket_ = socket(AF_INET6, SOCK_STREAM, 0);
     address_ = absl::StrCat("[::]:", port_);
@@ -429,12 +437,51 @@ class FakeTcpServer {
     return CONTINUE_READING;
   }
 
+  class FakeTcpServerPeer {
+   public:
+    explicit FakeTcpServerPeer(int fd) : fd_(fd) {}
+
+    ~FakeTcpServerPeer() { close(fd_); }
+
+    void MaybeContinueSendingSettings() {
+      // https://tools.ietf.org/html/rfc7540#section-4.1
+      const std::vector<uint8_t> kEmptyHttp2SettingsFrame = {
+          0x00, 0x00, 0x00,       // length
+          0x04,                   // settings type
+          0x00,                   // flags
+          0x00, 0x00, 0x00, 0x00  // stream identifier
+      };
+      if (total_bytes_sent_ < kEmptyHttp2SettingsFrame.size()) {
+        int bytes_to_send = kEmptyHttp2SettingsFrame.size() - total_bytes_sent_;
+        int bytes_sent =
+            send(fd_, kEmptyHttp2SettingsFrame.data() + total_bytes_sent_,
+                 bytes_to_send, 0);
+        if (bytes_sent < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
+          gpr_log(GPR_ERROR,
+                  "Fake TCP server encountered unexpected error:%d |%s| "
+                  "sending %d bytes on fd:%d",
+                  errno, strerror(errno), bytes_to_send, fd_);
+          GPR_ASSERT(0);
+        } else if (bytes_sent > 0) {
+          total_bytes_sent_ += bytes_sent;
+          GPR_ASSERT(total_bytes_sent_ <= kEmptyHttp2SettingsFrame.size());
+        }
+      }
+    }
+
+    int fd() { return fd_; }
+
+   private:
+    int fd_;
+    int total_bytes_sent_ = 0;
+  };
+
   // Run a loop that periodically, every 10 ms:
   //   1) Checks if there are any new TCP connections to accept.
   //   2) Checks if any data has arrived yet on established connections,
   //      and reads from them if so, processing the sockets as configured.
   static void RunServerLoop(FakeTcpServer* self) {
-    std::set<int> peers;
+    std::set<std::unique_ptr<FakeTcpServerPeer>> peers;
     while (!gpr_event_get(&self->stop_ev_)) {
       int p = accept(self->accept_socket_, nullptr, nullptr);
       if (p == -1 && errno != EAGAIN && errno != EWOULDBLOCK) {
@@ -449,17 +496,19 @@ class FakeTcpServer {
                   errno);
           abort();
         }
-        peers.insert(p);
+        peers.insert(absl::make_unique<FakeTcpServerPeer>(p));
       }
       auto it = peers.begin();
       while (it != peers.end()) {
-        int p = *it;
+        FakeTcpServerPeer* peer = (*it).get();
+        if (self->accept_mode_ == AcceptMode::kEagerlySendSettings) {
+          peer->MaybeContinueSendingSettings();
+        }
         char buf[100];
-        int bytes_received_size = recv(p, buf, 100, 0);
+        int bytes_received_size = recv(peer->fd(), buf, 100, 0);
         ProcessReadResult r =
-            self->process_read_cb_(bytes_received_size, errno, p);
+            self->process_read_cb_(bytes_received_size, errno, peer->fd());
         if (r == CLOSE_SOCKET) {
-          close(p);
           it = peers.erase(it);
         } else {
           GPR_ASSERT(r == CONTINUE_READING);
@@ -469,9 +518,6 @@ class FakeTcpServer {
       gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
                                    gpr_time_from_millis(10, GPR_TIMESPAN)));
     }
-    for (auto it = peers.begin(); it != peers.end(); it++) {
-      close(*it);
-    }
     close(self->accept_socket_);
   }
 
@@ -481,6 +527,7 @@ class FakeTcpServer {
   gpr_event stop_ev_;
   std::string address_;
   std::unique_ptr<std::thread> run_server_loop_thd_;
+  const AcceptMode accept_mode_;
   std::function<ProcessReadResult(int, int, int)> process_read_cb_;
 };
 
@@ -500,7 +547,10 @@ TEST(AltsConcurrentConnectivityTest,
   // RPCs at the fake handshake server would be inherently racey.
   FakeHandshakeServer fake_handshake_server(
       false /* check num concurrent rpcs */);
-  FakeTcpServer fake_tcp_server(
+  // The fake_backend_server emulates a secure (ALTS based) gRPC backend. So
+  // it waits for the client to send the first bytes.
+  FakeTcpServer fake_backend_server(
+      FakeTcpServer::AcceptMode::kWaitForClientToSendFirstBytes,
       FakeTcpServer::CloseSocketUponReceivingBytesFromPeer);
   {
     gpr_timespec test_deadline = grpc_timeout_seconds_to_deadline(20);
@@ -510,7 +560,7 @@ TEST(AltsConcurrentConnectivityTest,
     for (size_t i = 0; i < num_concurrent_connects; i++) {
       connect_loop_runners.push_back(
           std::unique_ptr<ConnectLoopRunner>(new ConnectLoopRunner(
-              fake_tcp_server.address(), fake_handshake_server.address(),
+              fake_backend_server.address(), fake_handshake_server.address(),
               10 /* per connect deadline seconds */, 3 /* loops */,
               GRPC_CHANNEL_TRANSIENT_FAILURE /* expected connectivity states */,
               0 /* reconnect_backoff_ms unset */)));
@@ -530,9 +580,16 @@ TEST(AltsConcurrentConnectivityTest,
  * fail fast when the ALTS handshake server fails incoming handshakes fast. */
 TEST(AltsConcurrentConnectivityTest,
      TestHandshakeFailsFastWhenHandshakeServerClosesConnectionAfterAccepting) {
+  // The fake_handshake_server emulates a broken ALTS handshaker, which
+  // is an insecure server. So send settings to the client eagerly.
   FakeTcpServer fake_handshake_server(
+      FakeTcpServer::AcceptMode::kEagerlySendSettings,
       FakeTcpServer::CloseSocketUponReceivingBytesFromPeer);
-  FakeTcpServer fake_tcp_server(FakeTcpServer::CloseSocketUponCloseFromPeer);
+  // The fake_backend_server emulates a secure (ALTS based) server, so wait
+  // for the client to send the first bytes.
+  FakeTcpServer fake_backend_server(
+      FakeTcpServer::AcceptMode::kWaitForClientToSendFirstBytes,
+      FakeTcpServer::CloseSocketUponCloseFromPeer);
   {
     gpr_timespec test_deadline = grpc_timeout_seconds_to_deadline(20);
     std::vector<std::unique_ptr<ConnectLoopRunner>> connect_loop_runners;
@@ -541,7 +598,7 @@ TEST(AltsConcurrentConnectivityTest,
     for (size_t i = 0; i < num_concurrent_connects; i++) {
       connect_loop_runners.push_back(
           std::unique_ptr<ConnectLoopRunner>(new ConnectLoopRunner(
-              fake_tcp_server.address(), fake_handshake_server.address(),
+              fake_backend_server.address(), fake_handshake_server.address(),
               10 /* per connect deadline seconds */, 2 /* loops */,
               GRPC_CHANNEL_TRANSIENT_FAILURE /* expected connectivity states */,
               0 /* reconnect_backoff_ms unset */)));
@@ -562,9 +619,16 @@ TEST(AltsConcurrentConnectivityTest,
  * the overall connection deadline kicks in. */
 TEST(AltsConcurrentConnectivityTest,
      TestHandshakeFailsFastWhenHandshakeServerHangsAfterAccepting) {
+  // fake_handshake_server emulates an insecure server, so send settings first.
+  // It will be unresponsive for the rest of the connection, though.
   FakeTcpServer fake_handshake_server(
+      FakeTcpServer::AcceptMode::kEagerlySendSettings,
+      FakeTcpServer::CloseSocketUponCloseFromPeer);
+  // fake_backend_server emulates an ALTS based server, so wait for the client
+  // to send the first bytes.
+  FakeTcpServer fake_backend_server(
+      FakeTcpServer::AcceptMode::kWaitForClientToSendFirstBytes,
       FakeTcpServer::CloseSocketUponCloseFromPeer);
-  FakeTcpServer fake_tcp_server(FakeTcpServer::CloseSocketUponCloseFromPeer);
   {
     gpr_timespec test_deadline = grpc_timeout_seconds_to_deadline(20);
     std::vector<std::unique_ptr<ConnectLoopRunner>> connect_loop_runners;
@@ -573,7 +637,7 @@ TEST(AltsConcurrentConnectivityTest,
     for (size_t i = 0; i < num_concurrent_connects; i++) {
       connect_loop_runners.push_back(
           std::unique_ptr<ConnectLoopRunner>(new ConnectLoopRunner(
-              fake_tcp_server.address(), fake_handshake_server.address(),
+              fake_backend_server.address(), fake_handshake_server.address(),
               10 /* per connect deadline seconds */, 2 /* loops */,
               GRPC_CHANNEL_TRANSIENT_FAILURE /* expected connectivity states */,
               100 /* reconnect_backoff_ms */)));

From a8aeb280beeadd6ea2da8d4a1c650277054a9a8f Mon Sep 17 00:00:00 2001
From: Micah Kornfield <micahk@google.com>
Date: Thu, 30 Jul 2020 19:57:51 -0700
Subject: [PATCH 17/38] Revert "Merge pull request #23652 from
 grpc/revert-23568-use_sources"

This is a rollback of rollback to depend on sources.json for boringssl.

This reverts commit eaa670510c7ec0013cc872d5dafc1fc08d4a459c, reversing
changes made to 5488e7b5cdf0695b709a898399e98cd70024b5b6.
---
 src/boringssl/gen_build_yaml.py | 59 ++++++++-------------------------
 1 file changed, 14 insertions(+), 45 deletions(-)

diff --git a/src/boringssl/gen_build_yaml.py b/src/boringssl/gen_build_yaml.py
index b54f53df095..eb4a59ae8b7 100755
--- a/src/boringssl/gen_build_yaml.py
+++ b/src/boringssl/gen_build_yaml.py
@@ -13,24 +13,16 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from __future__ import print_function
-import shutil
-import sys
+import json
 import os
+import sys
 import yaml
 
-sys.dont_write_bytecode = True
-
-boring_ssl_root = os.path.abspath(
+sources_path = os.path.abspath(
     os.path.join(os.path.dirname(sys.argv[0]),
-                 '../../third_party/boringssl-with-bazel/src'))
-sys.path.append(os.path.join(boring_ssl_root, 'util'))
-
-try:
-    import generate_build_files
-except ImportError:
-    print(yaml.dump({}))
-    sys.exit()
+                 '../../third_party/boringssl-with-bazel/sources.json'))
+with open(sources_path, 'r') as s:
+    sources = json.load(s)
 
 
 def map_dir(filename):
@@ -38,18 +30,19 @@ def map_dir(filename):
 
 
 class Grpc(object):
-    """Implements a "platform" in the sense of boringssl's generate_build_files.py"""
-    yaml = None
+    """Adapter for boring-SSL json sources files. """
 
-    def WriteFiles(self, files, asm_outputs):
-        test_binaries = ['ssl_test', 'crypto_test']
+    def __init__(self, sources):
+        self.yaml = None
+        self.WriteFiles(sources)
 
+    def WriteFiles(self, files):
+        test_binaries = ['ssl_test', 'crypto_test']
         self.yaml = {
             '#':
                 'generated with src/boringssl/gen_build_yaml.py',
             'raw_boringssl_build_output_for_debugging': {
                 'files': files,
-                'asm_outputs': asm_outputs,
             },
             'libs': [
                 {
@@ -120,29 +113,5 @@ class Grpc(object):
         }
 
 
-os.chdir(os.path.dirname(sys.argv[0]))
-os.mkdir('src')
-try:
-    for f in os.listdir(boring_ssl_root):
-        os.symlink(os.path.join(boring_ssl_root, f), os.path.join('src', f))
-
-    grpc_platform = Grpc()
-    # We use a hack to run boringssl's util/generate_build_files.py as part of this script.
-    # The call will populate "grpc_platform" with boringssl's source file metadata.
-    # As a side effect this script generates err_data.c and crypto_test_data.cc (requires golang)
-    # Both of these files are already available under third_party/boringssl-with-bazel
-    # so we don't need to generate them again, but there's no option to disable that behavior.
-    # - crypto_test_data.cc is required to run boringssl_crypto_test but we already
-    #   use the copy under third_party/boringssl-with-bazel so we just delete it
-    # - err_data.c is already under third_party/boringssl-with-bazel so we just delete it
-    generate_build_files.main([grpc_platform])
-
-    print(yaml.dump(grpc_platform.yaml))
-
-finally:
-    # we don't want err_data.c and crypto_test_data.cc (see comment above)
-    if os.path.exists('err_data.c'):
-        os.remove('err_data.c')
-    if os.path.exists('crypto_test_data.cc'):
-        os.remove('crypto_test_data.cc')
-    shutil.rmtree('src')
+grpc_platform = Grpc(sources)
+print(yaml.dump(grpc_platform.yaml))

From b5efeecf3117d9bd25b6a17524447d7af6eb9aa4 Mon Sep 17 00:00:00 2001
From: Micah Kornfield <micahk@google.com>
Date: Thu, 30 Jul 2020 20:16:03 -0700
Subject: [PATCH 18/38] Fallback to google3 path of sources.json if initial
 link doesn't work

---
 src/boringssl/gen_build_yaml.py | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/src/boringssl/gen_build_yaml.py b/src/boringssl/gen_build_yaml.py
index eb4a59ae8b7..0b869fa64f3 100755
--- a/src/boringssl/gen_build_yaml.py
+++ b/src/boringssl/gen_build_yaml.py
@@ -18,11 +18,19 @@ import os
 import sys
 import yaml
 
+run_dir = os.path.dirname(sys.argv[0])
 sources_path = os.path.abspath(
-    os.path.join(os.path.dirname(sys.argv[0]),
+    os.path.join(run_dir,
                  '../../third_party/boringssl-with-bazel/sources.json'))
-with open(sources_path, 'r') as s:
-    sources = json.load(s)
+try:
+    with open(sources_path, 'r') as s:
+        sources = json.load(s)
+except IOError:
+    sources_path = os.path.abspath(
+        os.path.join(run_dir,
+                     '../../../../third_party/openssl/boringssl/sources.json'))
+    with open(sources_path, 'r') as s:
+        sources = json.load(s)
 
 
 def map_dir(filename):

From 7948a5c690ced0fe2e37d3e80ceee73e0e35e5fd Mon Sep 17 00:00:00 2001
From: Esun Kim <veblush@google.com>
Date: Thu, 30 Jul 2020 23:14:47 -0700
Subject: [PATCH 19/38] Disable GPR_ABSEIL_SYNC on Apple platforms

---
 include/grpc/impl/codegen/port_platform.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/include/grpc/impl/codegen/port_platform.h b/include/grpc/impl/codegen/port_platform.h
index 91e3e39b0b4..33f8a1a59d6 100644
--- a/include/grpc/impl/codegen/port_platform.h
+++ b/include/grpc/impl/codegen/port_platform.h
@@ -34,7 +34,6 @@
 #if defined(__APPLE__)
 // This is disabled on Apple platforms because macos/grpc_basictests_c_cpp
 // fails with this. https://github.com/grpc/grpc/issues/23661
-#define GPR_ABSEIL_SYNC 0
 #else
 #define GPR_ABSEIL_SYNC 1
 #endif

From 5112d75a46effd4a89d955d81559877363f3e8b7 Mon Sep 17 00:00:00 2001
From: Esun Kim <veblush@google.com>
Date: Thu, 30 Jul 2020 23:43:02 -0700
Subject: [PATCH 20/38] Add TestEnvironment to tests

---
 test/cpp/qps/inproc_sync_unary_ping_pong_test.cc | 2 ++
 test/cpp/qps/qps_interarrival_test.cc            | 2 ++
 test/cpp/qps/qps_openloop_test.cc                | 1 +
 3 files changed, 5 insertions(+)

diff --git a/test/cpp/qps/inproc_sync_unary_ping_pong_test.cc b/test/cpp/qps/inproc_sync_unary_ping_pong_test.cc
index 6257e42ebf4..1e2caa2be6f 100644
--- a/test/cpp/qps/inproc_sync_unary_ping_pong_test.cc
+++ b/test/cpp/qps/inproc_sync_unary_ping_pong_test.cc
@@ -20,6 +20,7 @@
 
 #include <grpc/support/log.h>
 
+#include "test/core/util/test_config.h"
 #include "test/cpp/qps/benchmark_config.h"
 #include "test/cpp/qps/driver.h"
 #include "test/cpp/qps/report.h"
@@ -58,6 +59,7 @@ static void RunSynchronousUnaryPingPong() {
 }  // namespace grpc
 
 int main(int argc, char** argv) {
+  grpc::testing::TestEnvironment env(argc, argv);
   grpc::testing::InitTest(&argc, &argv, true);
 
   grpc::testing::RunSynchronousUnaryPingPong();
diff --git a/test/cpp/qps/qps_interarrival_test.cc b/test/cpp/qps/qps_interarrival_test.cc
index 2cc22e9985f..1f29cd72968 100644
--- a/test/cpp/qps/qps_interarrival_test.cc
+++ b/test/cpp/qps/qps_interarrival_test.cc
@@ -22,6 +22,7 @@
 // Use the C histogram rather than C++ to avoid depending on proto
 #include "test/core/util/histogram.h"
 
+#include "test/core/util/test_config.h"
 #include "test/cpp/qps/interarrival.h"
 #include "test/cpp/util/test_config.h"
 
@@ -52,6 +53,7 @@ static void RunTest(RandomDistInterface&& r, int threads,
 using grpc::testing::ExpDist;
 
 int main(int argc, char** argv) {
+  grpc::testing::TestEnvironment env(argc, argv);
   grpc::testing::InitTest(&argc, &argv, true);
 
   RunTest(ExpDist(10.0), 5, std::string("Exponential(10)"));
diff --git a/test/cpp/qps/qps_openloop_test.cc b/test/cpp/qps/qps_openloop_test.cc
index 68062e66f25..19ff02b71f3 100644
--- a/test/cpp/qps/qps_openloop_test.cc
+++ b/test/cpp/qps/qps_openloop_test.cc
@@ -62,6 +62,7 @@ static void RunQPS() {
 }  // namespace grpc
 
 int main(int argc, char** argv) {
+  grpc::testing::TestEnvironment env(argc, argv);
   grpc::testing::InitTest(&argc, &argv, true);
 
   grpc::testing::RunQPS();

From d4f3e05fef5163f14577065b7b32c98500e8cdca Mon Sep 17 00:00:00 2001
From: Jan Tattermusch <jtattermusch@google.com>
Date: Fri, 31 Jul 2020 10:21:02 +0200
Subject: [PATCH 21/38] switch C++ interop tests to cmake

---
 .../interoptest/grpc_interop_cxx/build_interop.sh          | 7 ++++---
 tools/run_tests/run_interop_tests.py                       | 6 +++---
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/tools/dockerfile/interoptest/grpc_interop_cxx/build_interop.sh b/tools/dockerfile/interoptest/grpc_interop_cxx/build_interop.sh
index ec1da444097..54c0fa89c86 100755
--- a/tools/dockerfile/interoptest/grpc_interop_cxx/build_interop.sh
+++ b/tools/dockerfile/interoptest/grpc_interop_cxx/build_interop.sh
@@ -32,8 +32,9 @@ cd /var/local/git/grpc
 mkdir -p /usr/local/share/grpc
 cp etc/roots.pem /usr/local/share/grpc/roots.pem
 
-# build C++ interop client & server
+# build C++ interop client, interop server and http2 interop client
+mkdir -p cmake/build
+cd cmake/build
+cmake -DgRPC_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=Release ../..
 make interop_client interop_server -j4
-
-# build C++ http2 client
 make http2_client -j4
diff --git a/tools/run_tests/run_interop_tests.py b/tools/run_tests/run_interop_tests.py
index 32e247553b0..0ee4d5d72fc 100755
--- a/tools/run_tests/run_interop_tests.py
+++ b/tools/run_tests/run_interop_tests.py
@@ -97,16 +97,16 @@ class CXXLanguage:
         self.safename = 'cxx'
 
     def client_cmd(self, args):
-        return ['bins/opt/interop_client'] + args
+        return ['cmake/build/interop_client'] + args
 
     def client_cmd_http2interop(self, args):
-        return ['bins/opt/http2_client'] + args
+        return ['cmake/build/http2_client'] + args
 
     def cloud_to_prod_env(self):
         return {}
 
     def server_cmd(self, args):
-        return ['bins/opt/interop_server'] + args
+        return ['cmake/build/interop_server'] + args
 
     def global_env(self):
         return {}

From e310d4366cee22265acddc81523819aa5ceebd5f Mon Sep 17 00:00:00 2001
From: Yash Tibrewal <yashkt@google.com>
Date: Fri, 31 Jul 2020 02:01:40 -0700
Subject: [PATCH 22/38] Receive SETTINGS frame on clients before declaring
 subchannel READY

---
 .../chttp2/client/chttp2_connector.cc         | 117 ++++++++++----
 .../chttp2/client/chttp2_connector.h          |  19 ++-
 .../chttp2/transport/chttp2_transport.cc      |   2 +-
 src/ruby/spec/generic/active_call_spec.rb     |  27 +++-
 test/core/end2end/bad_server_response_test.cc | 149 ++++++++++++------
 test/core/handshake/client_ssl.cc             |   4 +
 test/cpp/end2end/filter_end2end_test.cc       |   7 +-
 test/cpp/end2end/generic_end2end_test.cc      |  11 +-
 .../server_interceptors_end2end_test.cc       |   4 +-
 9 files changed, 239 insertions(+), 101 deletions(-)

diff --git a/src/core/ext/transport/chttp2/client/chttp2_connector.cc b/src/core/ext/transport/chttp2/client/chttp2_connector.cc
index b5593cb17db..1e930ca5fb5 100644
--- a/src/core/ext/transport/chttp2/client/chttp2_connector.cc
+++ b/src/core/ext/transport/chttp2/client/chttp2_connector.cc
@@ -129,6 +129,15 @@ void Chttp2Connector::StartHandshakeLocked() {
   endpoint_ = nullptr;  // Endpoint handed off to handshake manager.
 }
 
+namespace {
+void NullThenSchedClosure(const DebugLocation& location, grpc_closure** closure,
+                          grpc_error* error) {
+  grpc_closure* c = *closure;
+  *closure = nullptr;
+  ExecCtx::Run(location, c, error);
+}
+}  // namespace
+
 void Chttp2Connector::OnHandshakeDone(void* arg, grpc_error* error) {
   auto* args = static_cast<HandshakerArgs*>(arg);
   Chttp2Connector* self = static_cast<Chttp2Connector*>(args->user_data);
@@ -154,53 +163,99 @@ void Chttp2Connector::OnHandshakeDone(void* arg, grpc_error* error) {
         error = GRPC_ERROR_REF(error);
       }
       self->result_->Reset();
+      NullThenSchedClosure(DEBUG_LOCATION, &self->notify_, error);
     } else if (args->endpoint != nullptr) {
-      grpc_endpoint_delete_from_pollset_set(args->endpoint,
-                                            self->args_.interested_parties);
       self->result_->transport =
           grpc_create_chttp2_transport(args->args, args->endpoint, true);
       self->result_->socket_node =
           grpc_chttp2_transport_get_socket_node(self->result_->transport);
+      self->result_->channel_args = args->args;
       GPR_ASSERT(self->result_->transport != nullptr);
-      // TODO(roth): We ideally want to wait until we receive HTTP/2
-      // settings from the server before we consider the connection
-      // established.  If that doesn't happen before the connection
-      // timeout expires, then we should consider the connection attempt a
-      // failure and feed that information back into the backoff code.
-      // We could pass a notify_on_receive_settings callback to
-      // grpc_chttp2_transport_start_reading() to let us know when
-      // settings are received, but we would need to figure out how to use
-      // that information here.
-      //
-      // Unfortunately, we don't currently have a way to split apart the two
-      // effects of scheduling c->notify: we start sending RPCs immediately
-      // (which we want to do) and we consider the connection attempt successful
-      // (which we don't want to do until we get the notify_on_receive_settings
-      // callback from the transport).  If we could split those things
-      // apart, then we could start sending RPCs but then wait for our
-      // timeout before deciding if the connection attempt is successful.
-      // If the attempt is not successful, then we would tear down the
-      // transport and feed the failure back into the backoff code.
-      //
-      // In addition, even if we did that, we would probably not want to do
-      // so until after transparent retries is implemented.  Otherwise, any
-      // RPC that we attempt to send on the connection before the timeout
-      // would fail instead of being retried on a subsequent attempt.
+      self->endpoint_ = args->endpoint;
+      self->Ref().release();  // Ref held by OnReceiveSettings()
+      GRPC_CLOSURE_INIT(&self->on_receive_settings_, OnReceiveSettings, self,
+                        grpc_schedule_on_exec_ctx);
+      self->Ref().release();  // Ref held by OnTimeout()
       grpc_chttp2_transport_start_reading(self->result_->transport,
-                                          args->read_buffer, nullptr);
-      self->result_->channel_args = args->args;
+                                          args->read_buffer,
+                                          &self->on_receive_settings_);
+      GRPC_CLOSURE_INIT(&self->on_timeout_, OnTimeout, self,
+                        grpc_schedule_on_exec_ctx);
+      grpc_timer_init(&self->timer_, self->args_.deadline, &self->on_timeout_);
     } else {
       // If the handshaking succeeded but there is no endpoint, then the
       // handshaker may have handed off the connection to some external
       // code. Just verify that exit_early flag is set.
       GPR_DEBUG_ASSERT(args->exit_early);
+      NullThenSchedClosure(DEBUG_LOCATION, &self->notify_, error);
     }
-    grpc_closure* notify = self->notify_;
-    self->notify_ = nullptr;
-    ExecCtx::Run(DEBUG_LOCATION, notify, error);
     self->handshake_mgr_.reset();
   }
   self->Unref();
 }
 
+void Chttp2Connector::OnReceiveSettings(void* arg, grpc_error* error) {
+  Chttp2Connector* self = static_cast<Chttp2Connector*>(arg);
+  {
+    MutexLock lock(&self->mu_);
+    if (!self->notify_error_.has_value()) {
+      if (error != GRPC_ERROR_NONE) {
+        // Transport got an error while waiting on SETTINGS frame.
+        // TODO(yashykt): The following two lines should be moved to
+        // SubchannelConnector::Result::Reset()
+        grpc_transport_destroy(self->result_->transport);
+        grpc_channel_args_destroy(self->result_->channel_args);
+        self->result_->Reset();
+      }
+      self->MaybeNotify(GRPC_ERROR_REF(error));
+      grpc_timer_cancel(&self->timer_);
+    } else {
+      // OnTimeout() was already invoked. Call Notify() again so that notify_
+      // can be invoked.
+      self->MaybeNotify(GRPC_ERROR_NONE);
+    }
+  }
+  self->Unref();
+}
+
+void Chttp2Connector::OnTimeout(void* arg, grpc_error* error) {
+  Chttp2Connector* self = static_cast<Chttp2Connector*>(arg);
+  {
+    MutexLock lock(&self->mu_);
+    if (!self->notify_error_.has_value()) {
+      // The transport did not receive the settings frame in time. Destroy the
+      // transport.
+      // TODO(yashykt): The following two lines should be moved to
+      // SubchannelConnector::Result::Reset()
+      grpc_transport_destroy(self->result_->transport);
+      grpc_channel_args_destroy(self->result_->channel_args);
+      self->result_->Reset();
+      self->MaybeNotify(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "connection attempt timed out before receiving SETTINGS frame"));
+    } else {
+      // OnReceiveSettings() was already invoked. Call Notify() again so that
+      // notify_ can be invoked.
+      self->MaybeNotify(GRPC_ERROR_NONE);
+    }
+  }
+  self->Unref();
+}
+
+void Chttp2Connector::MaybeNotify(grpc_error* error) {
+  if (notify_error_.has_value()) {
+    GRPC_ERROR_UNREF(error);
+    NullThenSchedClosure(DEBUG_LOCATION, &notify_, notify_error_.value());
+    // Clear out the endpoint, since it is the responsibility of the transport
+    // to shut it down.
+    // Clear state for a new Connect().
+    grpc_endpoint_delete_from_pollset_set(endpoint_, args_.interested_parties);
+    // We do not destroy the endpoint here, since it is the responsibility of
+    // the transport to shut it down.
+    endpoint_ = nullptr;
+    notify_error_.reset();
+  } else {
+    notify_error_ = error;
+  }
+}
+
 }  // namespace grpc_core
diff --git a/src/core/ext/transport/chttp2/client/chttp2_connector.h b/src/core/ext/transport/chttp2/client/chttp2_connector.h
index 1ecd172bba1..37143c8f187 100644
--- a/src/core/ext/transport/chttp2/client/chttp2_connector.h
+++ b/src/core/ext/transport/chttp2/client/chttp2_connector.h
@@ -39,6 +39,19 @@ class Chttp2Connector : public SubchannelConnector {
   static void Connected(void* arg, grpc_error* error);
   void StartHandshakeLocked();
   static void OnHandshakeDone(void* arg, grpc_error* error);
+  static void OnReceiveSettings(void* arg, grpc_error* error);
+  static void OnTimeout(void* arg, grpc_error* error);
+
+  // We cannot invoke notify_ until both OnTimeout() and OnReceiveSettings()
+  // have been called since that is an indicator to the upper layer that we are
+  // done with the connection attempt. So, the notification process is broken
+  // into two steps. 1) Either OnTimeout() or OnReceiveSettings() gets invoked
+  // first. Whichever gets invoked, calls MaybeNotify() to set the result and
+  // triggers the other callback to be invoked. 2) When the other callback is
+  // invoked, we call MaybeNotify() again to actually invoke the notify_
+  // callback. Note that this only happens if the handshake is done and the
+  // connector is waiting on the SETTINGS frame.
+  void MaybeNotify(grpc_error* error);
 
   Mutex mu_;
   Args args_;
@@ -47,9 +60,13 @@ class Chttp2Connector : public SubchannelConnector {
   bool shutdown_ = false;
   bool connecting_ = false;
   // Holds the endpoint when first created before being handed off to
-  // the handshake manager.
+  // the handshake manager, and then again after handshake is done.
   grpc_endpoint* endpoint_ = nullptr;
   grpc_closure connected_;
+  grpc_closure on_receive_settings_;
+  grpc_timer timer_;
+  grpc_closure on_timeout_;
+  absl::optional<grpc_error*> notify_error_;
   RefCountedPtr<HandshakeManager> handshake_mgr_;
 };
 
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
index ddaed426bf6..a1c665f774d 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
@@ -594,7 +594,7 @@ static void close_transport_locked(grpc_chttp2_transport* t,
   }
   if (t->notify_on_receive_settings != nullptr) {
     grpc_core::ExecCtx::Run(DEBUG_LOCATION, t->notify_on_receive_settings,
-                            GRPC_ERROR_CANCELLED);
+                            GRPC_ERROR_REF(error));
     t->notify_on_receive_settings = nullptr;
   }
   GRPC_ERROR_UNREF(error);
diff --git a/src/ruby/spec/generic/active_call_spec.rb b/src/ruby/spec/generic/active_call_spec.rb
index 6b44b22acf7..34b58593d37 100644
--- a/src/ruby/spec/generic/active_call_spec.rb
+++ b/src/ruby/spec/generic/active_call_spec.rb
@@ -43,6 +43,16 @@ describe GRPC::ActiveCall do
     @server = new_core_server_for_testing(nil)
     server_port = @server.add_http2_port(host, :this_port_is_insecure)
     @server.start
+    @received_rpcs_queue = Queue.new
+    @server_thread = Thread.new do
+      begin
+        received_rpc = @server.request_call
+      rescue GRPC::Core::CallError, StandardError => e
+        # enqueue the exception in this case as a way to indicate the error
+        received_rpc = e
+      end
+      @received_rpcs_queue.push(received_rpc)
+    end
     @ch = GRPC::Core::Channel.new("0.0.0.0:#{server_port}", nil,
                                   :this_channel_is_insecure)
   end
@@ -50,6 +60,7 @@ describe GRPC::ActiveCall do
   after(:each) do
     @server.shutdown_and_notify(deadline)
     @server.close
+    @server_thread.join
   end
 
   describe 'restricted view methods' do
@@ -105,7 +116,7 @@ describe GRPC::ActiveCall do
       client_call.remote_send(msg)
 
       # check that server rpc new was received
-      recvd_rpc = @server.request_call
+      recvd_rpc = @received_rpcs_queue.pop
       expect(recvd_rpc).to_not eq nil
       recvd_call = recvd_rpc.call
 
@@ -130,7 +141,7 @@ describe GRPC::ActiveCall do
       client_call.remote_send(msg)
 
       # confirm that the message was marshalled
-      recvd_rpc =  @server.request_call
+      recvd_rpc =  @received_rpcs_queue.pop
       recvd_call = recvd_rpc.call
       server_ops = {
         CallOps::SEND_INITIAL_METADATA => nil
@@ -160,7 +171,7 @@ describe GRPC::ActiveCall do
         call.run_batch(CallOps::SEND_CLOSE_FROM_CLIENT => nil) if f == 1
 
         # confirm that the message was marshalled
-        recvd_rpc =  @server.request_call
+        recvd_rpc =  @received_rpcs_queue.pop
         recvd_call = recvd_rpc.call
         server_ops = {
           CallOps::SEND_INITIAL_METADATA => nil
@@ -321,7 +332,7 @@ describe GRPC::ActiveCall do
       call = make_test_call
       metadata = { k1: 'v1', k2: 'v2' }
       ActiveCall.client_invoke(call, metadata)
-      recvd_rpc =  @server.request_call
+      recvd_rpc =  @received_rpcs_queue.pop
       recvd_call = recvd_rpc.call
       expect(recvd_call).to_not be_nil
       expect(recvd_rpc.metadata).to_not be_nil
@@ -339,7 +350,7 @@ describe GRPC::ActiveCall do
       call = make_test_call
       ActiveCall.client_invoke(call)
 
-      recvd_rpc = @server.request_call
+      recvd_rpc = @received_rpcs_queue.pop
       server_call = ActiveCall.new(
         recvd_rpc.call,
         @pass_through,
@@ -405,7 +416,7 @@ describe GRPC::ActiveCall do
       client_call = make_test_call
       ActiveCall.client_invoke(client_call)
 
-      recvd_rpc = @server.request_call
+      recvd_rpc = @received_rpcs_queue.pop
       recvd_call = recvd_rpc.call
 
       server_call = ActiveCall.new(
@@ -575,7 +586,7 @@ describe GRPC::ActiveCall do
       @client_call = make_test_call
       @client_call.run_batch(CallOps::SEND_INITIAL_METADATA => {})
 
-      recvd_rpc = @server.request_call
+      recvd_rpc = @received_rpcs_queue.pop
       recvd_call = recvd_rpc.call
       @server_call = ActiveCall.new(
         recvd_call,
@@ -654,7 +665,7 @@ describe GRPC::ActiveCall do
   end
 
   def expect_server_to_be_invoked(**kw)
-    recvd_rpc =  @server.request_call
+    recvd_rpc =  @received_rpcs_queue.pop
     expect(recvd_rpc).to_not eq nil
     recvd_call = recvd_rpc.call
     recvd_call.run_batch(CallOps::SEND_INITIAL_METADATA => kw)
diff --git a/test/core/end2end/bad_server_response_test.cc b/test/core/end2end/bad_server_response_test.cc
index dd927a155d3..334196367ec 100644
--- a/test/core/end2end/bad_server_response_test.cc
+++ b/test/core/end2end/bad_server_response_test.cc
@@ -29,6 +29,7 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 
+#include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gprpp/host_port.h"
 #include "src/core/lib/gprpp/memory.h"
@@ -47,15 +48,16 @@
   "Content-Length: 0\n"                      \
   "Date: Tue, 07 Jun 2016 17:43:20 GMT\n\n"
 
-#define HTTP2_RESP(STATUS_CODE)          \
-  "\x00\x00\x00\x04\x00\x00\x00\x00\x00" \
-  "\x00\x00>\x01\x04\x00\x00\x00\x01"    \
-  "\x10\x0e"                             \
-  "content-length\x01"                   \
-  "0"                                    \
-  "\x10\x0c"                             \
-  "content-type\x10"                     \
-  "application/grpc"                     \
+#define HTTP2_SETTINGS_FRAME "\x00\x00\x00\x04\x00\x00\x00\x00\x00"
+
+#define HTTP2_RESP(STATUS_CODE)       \
+  "\x00\x00>\x01\x04\x00\x00\x00\x01" \
+  "\x10\x0e"                          \
+  "content-length\x01"                \
+  "0"                                 \
+  "\x10\x0c"                          \
+  "content-type\x10"                  \
+  "application/grpc"                  \
   "\x10\x07:status\x03" #STATUS_CODE
 
 #define UNPARSEABLE_RESP "Bad Request\n"
@@ -63,8 +65,6 @@
 #define HTTP2_DETAIL_MSG(STATUS_CODE) \
   "Received http2 header with status: " #STATUS_CODE
 
-#define HTTP1_DETAIL_MSG "Trying to connect an http1.x server"
-
 /* TODO(zyc) Check the content of incoming data instead of using this length */
 /* The 'bad' server will start sending responses after reading this amount of
  * data from the client. */
@@ -80,24 +80,32 @@ struct rpc_state {
   grpc_slice_buffer outgoing_buffer;
   grpc_endpoint* tcp;
   gpr_atm done_atm;
-  bool write_done;
+  bool http2_response;
+  bool send_settings;
   const char* response_payload;
   size_t response_payload_length;
+  bool connection_attempt_made;
 };
 
 static int server_port;
 static struct rpc_state state;
 static grpc_closure on_read;
+static grpc_closure on_writing_settings_frame;
 static grpc_closure on_write;
 
 static void* tag(intptr_t t) { return (void*)t; }
 
 static void done_write(void* /*arg*/, grpc_error* error) {
   GPR_ASSERT(error == GRPC_ERROR_NONE);
-
   gpr_atm_rel_store(&state.done_atm, 1);
 }
 
+static void done_writing_settings_frame(void* /* arg */, grpc_error* error) {
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
+  grpc_endpoint_read(state.tcp, &state.temp_incoming_buffer, &on_read,
+                     /*urgent=*/false);
+}
+
 static void handle_write() {
   grpc_slice slice = grpc_slice_from_copied_buffer(
       state.response_payload, state.response_payload_length);
@@ -108,7 +116,10 @@ static void handle_write() {
 }
 
 static void handle_read(void* /*arg*/, grpc_error* error) {
-  GPR_ASSERT(error == GRPC_ERROR_NONE);
+  if (error != GRPC_ERROR_NONE) {
+    gpr_log(GPR_ERROR, "handle_read error: %s", grpc_error_string(error));
+    return;
+  }
   state.incoming_data_length += state.temp_incoming_buffer.length;
 
   size_t i;
@@ -119,11 +130,14 @@ static void handle_read(void* /*arg*/, grpc_error* error) {
     gpr_free(dump);
   }
 
-  gpr_log(GPR_DEBUG, "got %" PRIuPTR " bytes, expected %" PRIuPTR " bytes",
+  gpr_log(GPR_DEBUG,
+          "got %" PRIuPTR " bytes, expected %" PRIuPTR
+          " bytes or a non-HTTP2 response to be sent",
           state.incoming_data_length,
           SERVER_INCOMING_DATA_LENGTH_LOWER_THRESHOLD);
   if (state.incoming_data_length >=
-      SERVER_INCOMING_DATA_LENGTH_LOWER_THRESHOLD) {
+          SERVER_INCOMING_DATA_LENGTH_LOWER_THRESHOLD ||
+      !state.http2_response) {
     handle_write();
   } else {
     grpc_endpoint_read(state.tcp, &state.temp_incoming_buffer, &on_read,
@@ -137,14 +151,26 @@ static void on_connect(void* arg, grpc_endpoint* tcp,
   gpr_free(acceptor);
   test_tcp_server* server = static_cast<test_tcp_server*>(arg);
   GRPC_CLOSURE_INIT(&on_read, handle_read, nullptr, grpc_schedule_on_exec_ctx);
+  GRPC_CLOSURE_INIT(&on_writing_settings_frame, done_writing_settings_frame,
+                    nullptr, grpc_schedule_on_exec_ctx);
   GRPC_CLOSURE_INIT(&on_write, done_write, nullptr, grpc_schedule_on_exec_ctx);
   grpc_slice_buffer_init(&state.temp_incoming_buffer);
   grpc_slice_buffer_init(&state.outgoing_buffer);
+  state.connection_attempt_made = true;
   state.tcp = tcp;
   state.incoming_data_length = 0;
   grpc_endpoint_add_to_pollset(tcp, server->pollset[0]);
-  grpc_endpoint_read(tcp, &state.temp_incoming_buffer, &on_read,
-                     /*urgent=*/false);
+  if (state.send_settings) {
+    // Send settings frame from server
+    grpc_slice slice = grpc_slice_from_static_buffer(
+        HTTP2_SETTINGS_FRAME, sizeof(HTTP2_SETTINGS_FRAME) - 1);
+    grpc_slice_buffer_add(&state.outgoing_buffer, slice);
+    grpc_endpoint_write(state.tcp, &state.outgoing_buffer,
+                        &on_writing_settings_frame, nullptr);
+  } else {
+    grpc_endpoint_read(state.tcp, &state.temp_incoming_buffer, &on_read,
+                       /*urgent=*/false);
+  }
 }
 
 static gpr_timespec n_sec_deadline(int seconds) {
@@ -166,13 +192,20 @@ static void start_rpc(int target_port, grpc_status_code expected_status,
   state.cq = grpc_completion_queue_create_for_next(nullptr);
   cqv = cq_verifier_create(state.cq);
   state.target = grpc_core::JoinHostPort("127.0.0.1", target_port);
+
   state.channel =
       grpc_insecure_channel_create(state.target.c_str(), nullptr, nullptr);
   grpc_slice host = grpc_slice_from_static_string("localhost");
+  // The default connect deadline is 20 seconds, so reduce the RPC deadline to 1
+  // second. This helps us verify - a) If the server responded with a non-HTTP2
+  // response, the connect fails immediately resulting in
+  // GRPC_STATUS_UNAVAILABLE instead of GRPC_STATUS_DEADLINE_EXCEEDED. b) If the
+  // server does not send a HTTP2 SETTINGs frame, the RPC fails with a
+  // DEADLINE_EXCEEDED.
   state.call = grpc_channel_create_call(
       state.channel, nullptr, GRPC_PROPAGATE_DEFAULTS, state.cq,
       grpc_slice_from_static_string("/Service/Method"), &host,
-      gpr_inf_future(GPR_CLOCK_REALTIME), nullptr);
+      n_sec_deadline(1), nullptr);
 
   grpc_metadata_array_init(&initial_metadata_recv);
   grpc_metadata_array_init(&trailing_metadata_recv);
@@ -214,6 +247,9 @@ static void start_rpc(int target_port, grpc_status_code expected_status,
                                                    expected_detail)));
   }
 
+  gpr_log(GPR_ERROR, "%s",
+          grpc_dump_slice(details, GPR_DUMP_ASCII | GPR_DUMP_HEX));
+
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_slice_unref(details);
@@ -241,7 +277,7 @@ typedef struct {
 
 static void actually_poll_server(void* arg) {
   poll_args* pa = static_cast<poll_args*>(arg);
-  gpr_timespec deadline = n_sec_deadline(10);
+  gpr_timespec deadline = n_sec_deadline(1);
   while (true) {
     bool done = gpr_atm_acq_load(&state.done_atm) != 0;
     gpr_timespec time_left =
@@ -251,7 +287,7 @@ static void actually_poll_server(void* arg) {
     if (done || gpr_time_cmp(time_left, gpr_time_0(GPR_TIMESPAN)) < 0) {
       break;
     }
-    test_tcp_server_poll(pa->server, 1000);
+    test_tcp_server_poll(pa->server, 100);
   }
   gpr_event_set(pa->signal_when_done, (void*)1);
   gpr_free(pa);
@@ -260,7 +296,7 @@ static void actually_poll_server(void* arg) {
 static grpc_core::Thread* poll_server_until_read_done(
     test_tcp_server* server, gpr_event* signal_when_done) {
   gpr_atm_rel_store(&state.done_atm, 0);
-  state.write_done = 0;
+  state.connection_attempt_made = false;
   poll_args* pa = static_cast<poll_args*>(gpr_malloc(sizeof(*pa)));
   pa->server = server;
   pa->signal_when_done = signal_when_done;
@@ -270,7 +306,8 @@ static grpc_core::Thread* poll_server_until_read_done(
   return th;
 }
 
-static void run_test(const char* response_payload,
+static void run_test(bool http2_response, bool send_settings,
+                     const char* response_payload,
                      size_t response_payload_length,
                      grpc_status_code expected_status,
                      const char* expected_detail) {
@@ -283,6 +320,8 @@ static void run_test(const char* response_payload,
   server_port = grpc_pick_unused_port_or_die();
   test_tcp_server_init(&test_server, on_connect, &test_server);
   test_tcp_server_start(&test_server, server_port);
+  state.http2_response = http2_response;
+  state.send_settings = send_settings;
   state.response_payload = response_payload;
   state.response_payload_length = response_payload_length;
 
@@ -292,7 +331,8 @@ static void run_test(const char* response_payload,
   start_rpc(server_port, expected_status, expected_detail);
   gpr_event_wait(&ev, gpr_inf_future(GPR_CLOCK_REALTIME));
   thdptr->Join();
-
+  /* Proof that the server accepted the TCP connection. */
+  GPR_ASSERT(state.connection_attempt_made == true);
   /* clean up */
   grpc_endpoint_shutdown(state.tcp,
                          GRPC_ERROR_CREATE_FROM_STATIC_STRING("Test Shutdown"));
@@ -309,43 +349,48 @@ int main(int argc, char** argv) {
   grpc_init();
 
   /* status defined in hpack static table */
-  run_test(HTTP2_RESP(204), sizeof(HTTP2_RESP(204)) - 1, GRPC_STATUS_UNKNOWN,
-           HTTP2_DETAIL_MSG(204));
-  run_test(HTTP2_RESP(206), sizeof(HTTP2_RESP(206)) - 1, GRPC_STATUS_UNKNOWN,
-           HTTP2_DETAIL_MSG(206));
-  run_test(HTTP2_RESP(304), sizeof(HTTP2_RESP(304)) - 1, GRPC_STATUS_UNKNOWN,
-           HTTP2_DETAIL_MSG(304));
-  run_test(HTTP2_RESP(400), sizeof(HTTP2_RESP(400)) - 1, GRPC_STATUS_INTERNAL,
-           HTTP2_DETAIL_MSG(400));
-  run_test(HTTP2_RESP(404), sizeof(HTTP2_RESP(404)) - 1,
+  run_test(true, true, HTTP2_RESP(204), sizeof(HTTP2_RESP(204)) - 1,
+           GRPC_STATUS_UNKNOWN, HTTP2_DETAIL_MSG(204));
+  run_test(true, true, HTTP2_RESP(206), sizeof(HTTP2_RESP(206)) - 1,
+           GRPC_STATUS_UNKNOWN, HTTP2_DETAIL_MSG(206));
+  run_test(true, true, HTTP2_RESP(304), sizeof(HTTP2_RESP(304)) - 1,
+           GRPC_STATUS_UNKNOWN, HTTP2_DETAIL_MSG(304));
+  run_test(true, true, HTTP2_RESP(400), sizeof(HTTP2_RESP(400)) - 1,
+           GRPC_STATUS_INTERNAL, HTTP2_DETAIL_MSG(400));
+  run_test(true, true, HTTP2_RESP(404), sizeof(HTTP2_RESP(404)) - 1,
            GRPC_STATUS_UNIMPLEMENTED, HTTP2_DETAIL_MSG(404));
-  run_test(HTTP2_RESP(500), sizeof(HTTP2_RESP(500)) - 1, GRPC_STATUS_UNKNOWN,
-           HTTP2_DETAIL_MSG(500));
+  run_test(true, true, HTTP2_RESP(500), sizeof(HTTP2_RESP(500)) - 1,
+           GRPC_STATUS_UNKNOWN, HTTP2_DETAIL_MSG(500));
 
   /* status not defined in hpack static table */
-  run_test(HTTP2_RESP(401), sizeof(HTTP2_RESP(401)) - 1,
+  run_test(true, true, HTTP2_RESP(401), sizeof(HTTP2_RESP(401)) - 1,
            GRPC_STATUS_UNAUTHENTICATED, HTTP2_DETAIL_MSG(401));
-  run_test(HTTP2_RESP(403), sizeof(HTTP2_RESP(403)) - 1,
+  run_test(true, true, HTTP2_RESP(403), sizeof(HTTP2_RESP(403)) - 1,
            GRPC_STATUS_PERMISSION_DENIED, HTTP2_DETAIL_MSG(403));
-  run_test(HTTP2_RESP(429), sizeof(HTTP2_RESP(429)) - 1,
+  run_test(true, true, HTTP2_RESP(429), sizeof(HTTP2_RESP(429)) - 1,
            GRPC_STATUS_UNAVAILABLE, HTTP2_DETAIL_MSG(429));
-  run_test(HTTP2_RESP(499), sizeof(HTTP2_RESP(499)) - 1, GRPC_STATUS_UNKNOWN,
-           HTTP2_DETAIL_MSG(499));
-  run_test(HTTP2_RESP(502), sizeof(HTTP2_RESP(502)) - 1,
+  run_test(true, true, HTTP2_RESP(499), sizeof(HTTP2_RESP(499)) - 1,
+           GRPC_STATUS_UNKNOWN, HTTP2_DETAIL_MSG(499));
+  run_test(true, true, HTTP2_RESP(502), sizeof(HTTP2_RESP(502)) - 1,
            GRPC_STATUS_UNAVAILABLE, HTTP2_DETAIL_MSG(502));
-  run_test(HTTP2_RESP(503), sizeof(HTTP2_RESP(503)) - 1,
+  run_test(true, true, HTTP2_RESP(503), sizeof(HTTP2_RESP(503)) - 1,
            GRPC_STATUS_UNAVAILABLE, HTTP2_DETAIL_MSG(503));
-  run_test(HTTP2_RESP(504), sizeof(HTTP2_RESP(504)) - 1,
+  run_test(true, true, HTTP2_RESP(504), sizeof(HTTP2_RESP(504)) - 1,
            GRPC_STATUS_UNAVAILABLE, HTTP2_DETAIL_MSG(504));
-
-  /* unparseable response */
-  run_test(UNPARSEABLE_RESP, sizeof(UNPARSEABLE_RESP) - 1, GRPC_STATUS_UNKNOWN,
-           nullptr);
-
-  /* http1 response */
-  run_test(HTTP1_RESP_400, sizeof(HTTP1_RESP_400) - 1, GRPC_STATUS_INTERNAL,
-           HTTP1_DETAIL_MSG);
-
+  /* unparseable response. RPC should fail immediately due to a connect failure.
+   */
+  run_test(false, false, UNPARSEABLE_RESP, sizeof(UNPARSEABLE_RESP) - 1,
+           GRPC_STATUS_UNAVAILABLE, nullptr);
+
+  /* http1 response. RPC should fail immediately due to a connect failure. */
+  run_test(false, false, HTTP1_RESP_400, sizeof(HTTP1_RESP_400) - 1,
+           GRPC_STATUS_UNAVAILABLE, nullptr);
+
+  /* http2 response without sending a SETTINGs frame. RPC should fail with
+   * DEADLINE_EXCEEDED since the RPC deadline is lower than the connection
+   * attempt deadline. */
+  run_test(true, false, HTTP2_RESP(404), sizeof(HTTP2_RESP(404)) - 1,
+           GRPC_STATUS_DEADLINE_EXCEEDED, nullptr);
   grpc_shutdown();
   return 0;
 }
diff --git a/test/core/handshake/client_ssl.cc b/test/core/handshake/client_ssl.cc
index e7faa69f964..8a72620bb42 100644
--- a/test/core/handshake/client_ssl.cc
+++ b/test/core/handshake/client_ssl.cc
@@ -198,6 +198,10 @@ static void server_thread(void* arg) {
     gpr_log(GPR_INFO, "Handshake successful.");
   }
 
+  // Send out the settings frame.
+  const char settings_frame[] = "\x00\x00\x00\x04\x00\x00\x00\x00\x00";
+  SSL_write(ssl, settings_frame, sizeof(settings_frame) - 1);
+
   // Wait until the client drops its connection.
   char buf;
   while (SSL_read(ssl, &buf, sizeof(buf)) > 0)
diff --git a/test/cpp/end2end/filter_end2end_test.cc b/test/cpp/end2end/filter_end2end_test.cc
index 04421d2765e..062a47a8bef 100644
--- a/test/cpp/end2end/filter_end2end_test.cc
+++ b/test/cpp/end2end/filter_end2end_test.cc
@@ -18,6 +18,7 @@
 
 #include <memory>
 #include <mutex>
+#include <thread>
 
 #include <grpc/grpc.h>
 #include <grpc/support/time.h>
@@ -184,6 +185,7 @@ class FilterEnd2endTest : public ::testing::Test {
 
       // The string needs to be long enough to test heap-based slice.
       send_request.set_message("Hello world. Hello world. Hello world.");
+      std::thread request_call([this]() { server_ok(4); });
       std::unique_ptr<GenericClientAsyncReaderWriter> call =
           generic_stub_->PrepareCall(&cli_ctx, kMethodName, &cli_cq_);
       call->StartCall(tag(1));
@@ -200,7 +202,7 @@ class FilterEnd2endTest : public ::testing::Test {
       generic_service_.RequestCall(&srv_ctx, &stream, srv_cq_.get(),
                                    srv_cq_.get(), tag(4));
 
-      verify_ok(srv_cq_.get(), 4, true);
+      request_call.join();
       EXPECT_EQ(server_host_, srv_ctx.host().substr(0, server_host_.length()));
       EXPECT_EQ(kMethodName, srv_ctx.method());
       ByteBuffer recv_buffer;
@@ -278,6 +280,7 @@ TEST_F(FilterEnd2endTest, SimpleBidiStreaming) {
 
   cli_ctx.set_compression_algorithm(GRPC_COMPRESS_GZIP);
   send_request.set_message("Hello");
+  std::thread request_call([this]() { server_ok(2); });
   std::unique_ptr<GenericClientAsyncReaderWriter> cli_stream =
       generic_stub_->PrepareCall(&cli_ctx, kMethodName, &cli_cq_);
   cli_stream->StartCall(tag(1));
@@ -286,7 +289,7 @@ TEST_F(FilterEnd2endTest, SimpleBidiStreaming) {
   generic_service_.RequestCall(&srv_ctx, &srv_stream, srv_cq_.get(),
                                srv_cq_.get(), tag(2));
 
-  verify_ok(srv_cq_.get(), 2, true);
+  request_call.join();
   EXPECT_EQ(server_host_, srv_ctx.host().substr(0, server_host_.length()));
   EXPECT_EQ(kMethodName, srv_ctx.method());
 
diff --git a/test/cpp/end2end/generic_end2end_test.cc b/test/cpp/end2end/generic_end2end_test.cc
index 60ea51875b1..0d39691bb60 100644
--- a/test/cpp/end2end/generic_end2end_test.cc
+++ b/test/cpp/end2end/generic_end2end_test.cc
@@ -140,6 +140,7 @@ class GenericEnd2endTest : public ::testing::Test {
 
       delete method_name;  // Make sure that this is not needed after invocation
 
+      std::thread request_call([this]() { server_ok(4); });
       call->StartCall(tag(1));
       client_ok(1);
       std::unique_ptr<ByteBuffer> send_buffer =
@@ -154,7 +155,7 @@ class GenericEnd2endTest : public ::testing::Test {
       generic_service_.RequestCall(&srv_ctx, &stream, srv_cq_.get(),
                                    srv_cq_.get(), tag(4));
 
-      verify_ok(srv_cq_.get(), 4, true);
+      request_call.join();
       EXPECT_EQ(server_host_, srv_ctx.host().substr(0, server_host_.length()));
       EXPECT_EQ(kMethodName, srv_ctx.method());
 
@@ -282,7 +283,7 @@ TEST_F(GenericEnd2endTest, SequentialUnaryRpcs) {
 
     std::unique_ptr<ByteBuffer> cli_send_buffer =
         SerializeToByteBuffer(&send_request);
-    // Use the same cq as server so that events can be polled in time.
+    std::thread request_call([this]() { server_ok(4); });
     std::unique_ptr<GenericClientAsyncResponseReader> call =
         generic_stub_->PrepareUnaryCall(&cli_ctx, kMethodName,
                                         *cli_send_buffer.get(), &cli_cq_);
@@ -293,8 +294,7 @@ TEST_F(GenericEnd2endTest, SequentialUnaryRpcs) {
 
     generic_service_.RequestCall(&srv_ctx, &stream, srv_cq_.get(),
                                  srv_cq_.get(), tag(4));
-
-    server_ok(4);
+    request_call.join();
     EXPECT_EQ(server_host_, srv_ctx.host().substr(0, server_host_.length()));
     EXPECT_EQ(kMethodName, srv_ctx.method());
 
@@ -337,6 +337,7 @@ TEST_F(GenericEnd2endTest, SimpleBidiStreaming) {
 
   cli_ctx.set_compression_algorithm(GRPC_COMPRESS_GZIP);
   send_request.set_message("Hello");
+  std::thread request_call([this]() { server_ok(2); });
   std::unique_ptr<GenericClientAsyncReaderWriter> cli_stream =
       generic_stub_->PrepareCall(&cli_ctx, kMethodName, &cli_cq_);
   cli_stream->StartCall(tag(1));
@@ -344,8 +345,8 @@ TEST_F(GenericEnd2endTest, SimpleBidiStreaming) {
 
   generic_service_.RequestCall(&srv_ctx, &srv_stream, srv_cq_.get(),
                                srv_cq_.get(), tag(2));
+  request_call.join();
 
-  verify_ok(srv_cq_.get(), 2, true);
   EXPECT_EQ(server_host_, srv_ctx.host().substr(0, server_host_.length()));
   EXPECT_EQ(kMethodName, srv_ctx.method());
 
diff --git a/test/cpp/end2end/server_interceptors_end2end_test.cc b/test/cpp/end2end/server_interceptors_end2end_test.cc
index e8e0435a960..25e64f7580d 100644
--- a/test/cpp/end2end/server_interceptors_end2end_test.cc
+++ b/test/cpp/end2end/server_interceptors_end2end_test.cc
@@ -536,6 +536,8 @@ TEST_F(ServerInterceptorsAsyncEnd2endTest, GenericRPCTest) {
   send_request.set_message("Hello");
   cli_ctx.AddMetadata("testkey", "testvalue");
 
+  CompletionQueue* cq = srv_cq.get();
+  std::thread request_call([cq]() { Verifier().Expect(4, true).Verify(cq); });
   std::unique_ptr<GenericClientAsyncReaderWriter> call =
       generic_stub.PrepareCall(&cli_ctx, kMethodName, &cli_cq);
   call->StartCall(tag(1));
@@ -551,7 +553,7 @@ TEST_F(ServerInterceptorsAsyncEnd2endTest, GenericRPCTest) {
 
   service.RequestCall(&srv_ctx, &stream, srv_cq.get(), srv_cq.get(), tag(4));
 
-  Verifier().Expect(4, true).Verify(srv_cq.get());
+  request_call.join();
   EXPECT_EQ(kMethodName, srv_ctx.method());
   EXPECT_TRUE(CheckMetadata(srv_ctx.client_metadata(), "testkey", "testvalue"));
   srv_ctx.AddTrailingMetadata("testkey", "testvalue");

From b1ab3c1217ea9b3e305eba58e02aad0500884513 Mon Sep 17 00:00:00 2001
From: Jan Tattermusch <jtattermusch@google.com>
Date: Fri, 31 Jul 2020 13:08:43 +0200
Subject: [PATCH 23/38] add cmake to grpc_interop_cxx docker image

---
 .../interoptest/grpc_interop_cxx/Dockerfile.template     | 1 +
 tools/dockerfile/interoptest/grpc_interop_cxx/Dockerfile | 9 +++++++++
 2 files changed, 10 insertions(+)

diff --git a/templates/tools/dockerfile/interoptest/grpc_interop_cxx/Dockerfile.template b/templates/tools/dockerfile/interoptest/grpc_interop_cxx/Dockerfile.template
index dbb3ffc9bb2..89ddb2c24bb 100644
--- a/templates/tools/dockerfile/interoptest/grpc_interop_cxx/Dockerfile.template
+++ b/templates/tools/dockerfile/interoptest/grpc_interop_cxx/Dockerfile.template
@@ -19,6 +19,7 @@
   <%include file="../../apt_get_basic.include"/>
   <%include file="../../python_deps.include"/>
   <%include file="../../cxx_deps.include"/>
+  <%include file="../../cmake_jessie_backports.include"/>
   <%include file="../../run_tests_addons.include"/>
   # Define the default command.
   CMD ["bash"]
diff --git a/tools/dockerfile/interoptest/grpc_interop_cxx/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_cxx/Dockerfile
index 477618e5767..40b56cc8198 100644
--- a/tools/dockerfile/interoptest/grpc_interop_cxx/Dockerfile
+++ b/tools/dockerfile/interoptest/grpc_interop_cxx/Dockerfile
@@ -70,6 +70,15 @@ RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 t
 # C++ dependencies
 RUN apt-get update && apt-get -y install libgflags-dev libgtest-dev libc++-dev clang && apt-get clean
 
+#=================
+# Use cmake 3.6 from jessie-backports
+# should only be used for images based on debian jessie.
+
+RUN echo "deb http://archive.debian.org/debian jessie-backports main" | tee /etc/apt/sources.list.d/jessie-backports.list
+RUN echo 'Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf
+RUN sed -i '/deb http:\/\/deb.debian.org\/debian jessie-updates main/d' /etc/apt/sources.list
+RUN apt-get update && apt-get install -t jessie-backports -y cmake && apt-get clean
+
 
 RUN mkdir /var/local/jenkins
 

From 0aa52ab85e9dd1d24ca3f121311bca5538f33e6a Mon Sep 17 00:00:00 2001
From: Yash Tibrewal <yashkt@google.com>
Date: Fri, 31 Jul 2020 10:56:37 -0700
Subject: [PATCH 24/38] Remove debug logging

---
 test/core/end2end/bad_server_response_test.cc | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/test/core/end2end/bad_server_response_test.cc b/test/core/end2end/bad_server_response_test.cc
index 334196367ec..56419a24647 100644
--- a/test/core/end2end/bad_server_response_test.cc
+++ b/test/core/end2end/bad_server_response_test.cc
@@ -247,9 +247,6 @@ static void start_rpc(int target_port, grpc_status_code expected_status,
                                                    expected_detail)));
   }
 
-  gpr_log(GPR_ERROR, "%s",
-          grpc_dump_slice(details, GPR_DUMP_ASCII | GPR_DUMP_HEX));
-
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_slice_unref(details);

From 41d87b73076fdd35e8c893787c9bc119ea28dd96 Mon Sep 17 00:00:00 2001
From: "Mark D. Roth" <roth@google.com>
Date: Fri, 31 Jul 2020 09:10:17 -0700
Subject: [PATCH 25/38] Allow C++-style comments on #endif lines.

---
 .../filters/client_channel/client_channel.h   |  2 +-
 tools/distrib/check_include_guards.py         | 43 ++++++++-----------
 2 files changed, 20 insertions(+), 25 deletions(-)

diff --git a/src/core/ext/filters/client_channel/client_channel.h b/src/core/ext/filters/client_channel/client_channel.h
index 2ba2b87bb13..35c2a2af6b2 100644
--- a/src/core/ext/filters/client_channel/client_channel.h
+++ b/src/core/ext/filters/client_channel/client_channel.h
@@ -79,4 +79,4 @@ void grpc_client_channel_stop_connectivity_watch(
 grpc_core::RefCountedPtr<grpc_core::SubchannelCall>
 grpc_client_channel_get_subchannel_call(grpc_call_element* elem);
 
-#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_H */
+#endif  // GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_H
diff --git a/tools/distrib/check_include_guards.py b/tools/distrib/check_include_guards.py
index 2983441176d..21a90383589 100755
--- a/tools/distrib/check_include_guards.py
+++ b/tools/distrib/check_include_guards.py
@@ -43,13 +43,14 @@ class GuardValidator(object):
     def __init__(self):
         self.ifndef_re = re.compile(r'#ifndef ([A-Z][A-Z_1-9]*)')
         self.define_re = re.compile(r'#define ([A-Z][A-Z_1-9]*)')
-        self.endif_c_re = re.compile(
+        self.endif_c_core_re = re.compile(
             r'#endif /\* (?: *\\\n *)?([A-Z][A-Z_1-9]*) (?:\\\n *)?\*/$')
-        self.endif_cpp_re = re.compile(r'#endif  // ([A-Z][A-Z_1-9]*)')
+        self.endif_re = re.compile(r'#endif  // ([A-Z][A-Z_1-9]*)')
         self.failed = False
 
     def fail(self, fpath, regexp, fcontents, match_txt, correct, fix):
-        cpp_header = 'grpc++' in fpath or 'grpcpp' in fpath
+        c_core_header = 'include' in fpath and not ('grpc++' in fpath or
+                                                    'grpcpp' in fpath)
         self.failed = True
         invalid_guards_msg_template = (
             '{0}: Missing preprocessor guards (RE {1}). '
@@ -58,7 +59,8 @@ class GuardValidator(object):
             '#define {2}\n'
             '...\n'
             '... epic code ...\n'
-            '...\n') + ('#endif  // {2}' if cpp_header else '#endif /* {2} */')
+            '...\n') + ('#endif /* {2} */'
+                        if c_core_header else '#endif  // {2}')
         if not match_txt:
             print invalid_guards_msg_template.format(fpath, regexp.pattern,
                                                      build_valid_guard(fpath))
@@ -79,7 +81,8 @@ class GuardValidator(object):
         return fcontents
 
     def check(self, fpath, fix):
-        cpp_header = 'grpc++' in fpath or 'grpcpp' in fpath
+        c_core_header = 'include' in fpath and not ('grpc++' in fpath or
+                                                    'grpcpp' in fpath)
         valid_guard = build_valid_guard(fpath)
 
         fcontents = load(fpath)
@@ -120,22 +123,27 @@ class GuardValidator(object):
             if fix: save(fpath, fcontents)
 
         # Is there a properly commented #endif?
-        endif_re = self.endif_cpp_re if cpp_header else self.endif_c_re
         flines = fcontents.rstrip().splitlines()
-        match = endif_re.search('\n'.join(flines[-3:]))
+        match = self.endif_c_core_re.search('\n'.join(flines[-3:]))
+        if not match and not c_core_header:
+            match = self.endif_re.search('\n'.join(flines[-3:]))
         if not match:
             # No endif. Check if we have the last line as just '#endif' and if so
             # replace it with a properly commented one.
             if flines[-1] == '#endif':
-                flines[-1] = ('#endif' +
-                              ('  // {}\n'.format(valid_guard) if cpp_header
-                               else ' /* {} */\n'.format(valid_guard)))
+                flines[-1] = (
+                    '#endif' +
+                    (' /* {} */\n'.format(valid_guard)
+                     if c_core_header else '  // {}\n'.format(valid_guard)))
                 if fix:
                     fcontents = '\n'.join(flines)
                     save(fpath, fcontents)
             else:
                 # something else is wrong, bail out
-                self.fail(fpath, endif_re, flines[-1], '', '', False)
+                self.fail(
+                    fpath,
+                    self.endif_c_core_re if c_core_header else self.endif_re,
+                    flines[-1], '', '', False)
         elif match.group(1) != running_guard:
             # Is the #endif guard the same as the #ifndef and #define guards?
             fcontents = self.fail(fpath, endif_re, fcontents, match.group(1),
@@ -155,18 +163,6 @@ argp.add_argument('-f', '--fix', default=False, action='store_true')
 argp.add_argument('--precommit', default=False, action='store_true')
 args = argp.parse_args()
 
-KNOWN_BAD = set([
-    'src/core/ext/filters/client_channel/health/health.pb.h',
-    'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h',
-    'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/duration.pb.h',
-    'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/timestamp.pb.h',
-    'src/core/tsi/alts/handshaker/altscontext.pb.h',
-    'src/core/tsi/alts/handshaker/handshaker.pb.h',
-    'src/core/tsi/alts/handshaker/transport_security_common.pb.h',
-    'include/grpc++/ext/reflection.grpc.pb.h',
-    'include/grpc++/ext/reflection.pb.h',
-])
-
 grep_filter = r"grep -E '^(include|src/core)/.*\.h$'"
 if args.precommit:
     git_command = 'git diff --name-only HEAD'
@@ -189,7 +185,6 @@ except subprocess.CalledProcessError:
 validator = GuardValidator()
 
 for filename in filename_list:
-    if filename in KNOWN_BAD: continue
     # Skip check for upb generated code.
     if filename.endswith('.upb.h') or filename.endswith('.upb.c'):
         continue

From 94a85b051d9848e8e2a1506cc80396a046cc122c Mon Sep 17 00:00:00 2001
From: Vijay Pai <vpai@google.com>
Date: Fri, 31 Jul 2020 14:26:17 -0700
Subject: [PATCH 26/38] Expand comment to address reviewer feedback

---
 test/cpp/microbenchmarks/bm_cq.cc | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/test/cpp/microbenchmarks/bm_cq.cc b/test/cpp/microbenchmarks/bm_cq.cc
index 1640c4d52f0..8ab0450f4ee 100644
--- a/test/cpp/microbenchmarks/bm_cq.cc
+++ b/test/cpp/microbenchmarks/bm_cq.cc
@@ -211,7 +211,12 @@ static void BM_Callback_CQ_Pass1Core(benchmark::State& state) {
   bool got_shutdown = false;
   ShutdownCallback shutdown_cb(&got_shutdown);
   // This test with stack-allocated completions only works for non-polling or
-  // EM-polling callback core CQs. For generality, test with non-polling.
+  // EM-polling callback core CQs because otherwise the callback could execute
+  // on  another thread after the stack objects here go out of scope. An
+  // alternative would be to synchronize between the benchmark loop and the
+  // callback, but then it would be measuring the overhead of synchronization
+  // rather than the overhead of the completion queue.
+  // For generality, test here with non-polling.
   grpc_completion_queue_attributes attr;
   attr.version = 2;
   attr.cq_completion_type = GRPC_CQ_CALLBACK;

From 35d3e7c5315eb9bb7c322e01dec8d4cb0b9d0f76 Mon Sep 17 00:00:00 2001
From: "Mark D. Roth" <roth@google.com>
Date: Mon, 3 Aug 2020 09:50:01 -0700
Subject: [PATCH 27/38] Add per-address attribute mechanism that does not
 affect subchannel uniqueness.

---
 .../filters/client_channel/server_address.cc  | 47 ++++++++++++++++---
 .../filters/client_channel/server_address.h   | 46 ++++++++++++++++--
 2 files changed, 82 insertions(+), 11 deletions(-)

diff --git a/src/core/ext/filters/client_channel/server_address.cc b/src/core/ext/filters/client_channel/server_address.cc
index 93d361a8154..16ec96b7b56 100644
--- a/src/core/ext/filters/client_channel/server_address.cc
+++ b/src/core/ext/filters/client_channel/server_address.cc
@@ -26,23 +26,56 @@ namespace grpc_core {
 // ServerAddress
 //
 
-ServerAddress::ServerAddress(const grpc_resolved_address& address,
-                             grpc_channel_args* args)
-    : address_(address), args_(args) {}
+ServerAddress::ServerAddress(
+    const grpc_resolved_address& address, grpc_channel_args* args,
+    std::map<const char*, std::unique_ptr<AttributeInterface>> attributes)
+    : address_(address), args_(args), attributes_(std::move(attributes)) {}
 
-ServerAddress::ServerAddress(const void* address, size_t address_len,
-                             grpc_channel_args* args)
-    : args_(args) {
+ServerAddress::ServerAddress(
+    const void* address, size_t address_len, grpc_channel_args* args,
+    std::map<const char*, std::unique_ptr<AttributeInterface>> attributes)
+    : args_(args), attributes_(std::move(attributes)) {
   memcpy(address_.addr, address, address_len);
   address_.len = static_cast<socklen_t>(address_len);
 }
 
+namespace {
+
+int CompareAttributes(
+    const std::map<const char*,
+                   std::unique_ptr<ServerAddress::AttributeInterface>>&
+        attributes1,
+    const std::map<const char*,
+                   std::unique_ptr<ServerAddress::AttributeInterface>>&
+        attributes2) {
+  auto it2 = attributes2.begin();
+  for (auto it1 = attributes1.begin(); it1 != attributes1.end(); ++it1) {
+    // attributes2 has fewer elements than attributes1
+    if (it2 == attributes2.end()) return -1;
+    // compare keys
+    int retval = strcmp(it1->first, it2->first);
+    if (retval != 0) return retval;
+    // compare values
+    retval = it1->second->Cmp(it2->second.get());
+    if (retval != 0) return retval;
+    ++it2;
+  }
+  // attributes1 has fewer elements than attributes2
+  if (it2 != attributes2.end()) return 1;
+  // equal
+  return 0;
+}
+
+}  // namespace
+
 int ServerAddress::Cmp(const ServerAddress& other) const {
   if (address_.len > other.address_.len) return 1;
   if (address_.len < other.address_.len) return -1;
   int retval = memcmp(address_.addr, other.address_.addr, address_.len);
   if (retval != 0) return retval;
-  return grpc_channel_args_compare(args_, other.args_);
+  retval = grpc_channel_args_compare(args_, other.args_);
+  if (retval != 0) return retval;
+  return CompareAttributes(attributes_, other.attributes_);
 }
 
 }  // namespace grpc_core
diff --git a/src/core/ext/filters/client_channel/server_address.h b/src/core/ext/filters/client_channel/server_address.h
index f53dd3c7dd5..4a28ea75205 100644
--- a/src/core/ext/filters/client_channel/server_address.h
+++ b/src/core/ext/filters/client_channel/server_address.h
@@ -21,6 +21,8 @@
 
 #include <grpc/support/port_platform.h>
 
+#include <map>
+
 #include "absl/container/inlined_vector.h"
 
 #include "src/core/lib/channel/channel_args.h"
@@ -37,26 +39,54 @@ namespace grpc_core {
 // args when a subchannel is created for this address.
 class ServerAddress {
  public:
+  // Base clas for resolver-supplied attributes.
+  // Unlike channel args, these attributes don't affect subchannel
+  // uniqueness or behavior.  They are for use by LB policies only.
+  class AttributeInterface {
+   public:
+    virtual ~AttributeInterface();
+
+    // Creates a copy of the attribute.
+    virtual std::unique_ptr<AttributeInterface> Copy() const = 0;
+
+    // Compares this attribute with another.
+    virtual int Cmp(const AttributeInterface* other) const = 0;
+  };
+
   // Takes ownership of args.
-  ServerAddress(const grpc_resolved_address& address, grpc_channel_args* args);
+  ServerAddress(const grpc_resolved_address& address, grpc_channel_args* args,
+                std::map<const char*, std::unique_ptr<AttributeInterface>>
+                    attributes = {});
   ServerAddress(const void* address, size_t address_len,
-                grpc_channel_args* args);
+                grpc_channel_args* args,
+                std::map<const char*, std::unique_ptr<AttributeInterface>>
+                    attributes = {});
 
   ~ServerAddress() { grpc_channel_args_destroy(args_); }
 
   // Copyable.
   ServerAddress(const ServerAddress& other)
-      : address_(other.address_), args_(grpc_channel_args_copy(other.args_)) {}
+      : address_(other.address_), args_(grpc_channel_args_copy(other.args_)) {
+    for (const auto& p : other.attributes_) {
+      attributes_[p.first] = p.second->Copy();
+    }
+  }
   ServerAddress& operator=(const ServerAddress& other) {
     address_ = other.address_;
     grpc_channel_args_destroy(args_);
     args_ = grpc_channel_args_copy(other.args_);
+    attributes_.clear();
+    for (const auto& p : other.attributes_) {
+      attributes_[p.first] = p.second->Copy();
+    }
     return *this;
   }
 
   // Movable.
   ServerAddress(ServerAddress&& other)
-      : address_(other.address_), args_(other.args_) {
+      : address_(other.address_),
+        args_(other.args_),
+        attributes_(std::move(other.attributes_)) {
     other.args_ = nullptr;
   }
   ServerAddress& operator=(ServerAddress&& other) {
@@ -64,6 +94,7 @@ class ServerAddress {
     grpc_channel_args_destroy(args_);
     args_ = other.args_;
     other.args_ = nullptr;
+    attributes_ = std::move(other.attributes_);
     return *this;
   }
 
@@ -74,9 +105,16 @@ class ServerAddress {
   const grpc_resolved_address& address() const { return address_; }
   const grpc_channel_args* args() const { return args_; }
 
+  const AttributeInterface* GetAttribute(const char* key) const {
+    auto it = attributes_.find(key);
+    if (it == attributes_.end()) return nullptr;
+    return it->second.get();
+  }
+
  private:
   grpc_resolved_address address_;
   grpc_channel_args* args_;
+  std::map<const char*, std::unique_ptr<AttributeInterface>> attributes_;
 };
 
 //

From d0fbf25216bd337d20aceee7d91c72f0e0135d62 Mon Sep 17 00:00:00 2001
From: Lidi Zheng <lidiz@google.com>
Date: Mon, 3 Aug 2020 10:58:18 -0700
Subject: [PATCH 28/38] Resolve the user warning generated by our setup.py

---
 setup.py | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/setup.py b/setup.py
index 2fb0296506e..90df0a1042e 100644
--- a/setup.py
+++ b/setup.py
@@ -12,6 +12,11 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 """A setup module for the GRPC Python package."""
+
+# setuptools need to be imported before distutils. Otherwise it might lead to
+# undesirable behaviors or errors.
+import setuptools
+
 from distutils import cygwinccompiler
 from distutils import extension as _extension
 from distutils import util
@@ -25,7 +30,6 @@ import shutil
 import sys
 import sysconfig
 
-import setuptools
 from setuptools.command import egg_info
 
 import subprocess

From 5d73e3af27a77031ce6c2e67e9a5c00e60272dcf Mon Sep 17 00:00:00 2001
From: Karthik Ravi Shankar <karthikrs@google.com>
Date: Mon, 3 Aug 2020 11:21:27 -0700
Subject: [PATCH 29/38] Remove error_details_impl.h

https://github.com/grpc/grpc/pull/23601 should have removed
error_details_impl.h, but it did not.

This PR fixes this oversight.
---
 include/grpcpp/support/error_details_impl.h | 48 ---------------------
 1 file changed, 48 deletions(-)
 delete mode 100644 include/grpcpp/support/error_details_impl.h

diff --git a/include/grpcpp/support/error_details_impl.h b/include/grpcpp/support/error_details_impl.h
deleted file mode 100644
index ae5f04ced8f..00000000000
--- a/include/grpcpp/support/error_details_impl.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- *
- * Copyright 2017 gRPC authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-#ifndef GRPCPP_SUPPORT_ERROR_DETAILS_IMPL_H
-#define GRPCPP_SUPPORT_ERROR_DETAILS_IMPL_H
-
-#include <grpcpp/support/status.h>
-
-namespace google {
-namespace rpc {
-class Status;
-}  // namespace rpc
-}  // namespace google
-
-namespace grpc_impl {
-
-/// Map a \a grpc::Status to a \a google::rpc::Status.
-/// The given \a to object will be cleared.
-/// On success, returns status with OK.
-/// Returns status with \a INVALID_ARGUMENT, if failed to deserialize.
-/// Returns status with \a FAILED_PRECONDITION, if \a to is nullptr.
-grpc::Status ExtractErrorDetails(const grpc::Status& from,
-                                 ::google::rpc::Status* to);
-
-/// Map \a google::rpc::Status to a \a grpc::Status.
-/// Returns OK on success.
-/// Returns status with \a FAILED_PRECONDITION if \a to is nullptr.
-grpc::Status SetErrorDetails(const ::google::rpc::Status& from,
-                             grpc::Status* to);
-
-}  // namespace grpc_impl
-
-#endif  // GRPCPP_SUPPORT_ERROR_DETAILS_IMPL_H

From 981baff79b358e49765075680818a911312384d4 Mon Sep 17 00:00:00 2001
From: Lidi Zheng <lidiz@google.com>
Date: Mon, 3 Aug 2020 11:28:56 -0700
Subject: [PATCH 30/38] Fix the docgen script: 1. Make it use python3 by
 default; 2. Simplify its arguments.

---
 build_autogenerated.yaml                      |  1 -
 .../distrib/python/grpc_version.py.template   | 19 +++++
 tools/distrib/grpc_version.py                 | 17 ++++
 tools/distrib/python/docgen.py                | 82 +++++++------------
 tools/distrib/python/grpc_version.py          | 17 ++++
 5 files changed, 82 insertions(+), 54 deletions(-)
 create mode 100644 templates/tools/distrib/python/grpc_version.py.template
 create mode 100644 tools/distrib/grpc_version.py
 create mode 100644 tools/distrib/python/grpc_version.py

diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml
index 115097a3749..9bd597bad31 100644
--- a/build_autogenerated.yaml
+++ b/build_autogenerated.yaml
@@ -2276,7 +2276,6 @@ libs:
   public_headers:
   - include/grpc++/support/error_details.h
   - include/grpcpp/support/error_details.h
-  - include/grpcpp/support/error_details_impl.h
   headers: []
   src:
   - src/proto/grpc/status/status.proto
diff --git a/templates/tools/distrib/python/grpc_version.py.template b/templates/tools/distrib/python/grpc_version.py.template
new file mode 100644
index 00000000000..a2f471aff74
--- /dev/null
+++ b/templates/tools/distrib/python/grpc_version.py.template
@@ -0,0 +1,19 @@
+%YAML 1.2
+--- |
+  # Copyright 2020 gRPC authors.
+  #
+  # Licensed under the Apache License, Version 2.0 (the "License");
+  # you may not use this file except in compliance with the License.
+  # You may obtain a copy of the License at
+  #
+  #     http://www.apache.org/licenses/LICENSE-2.0
+  #
+  # Unless required by applicable law or agreed to in writing, software
+  # distributed under the License is distributed on an "AS IS" BASIS,
+  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  # See the License for the specific language governing permissions and
+  # limitations under the License.
+
+  # AUTO-GENERATED FROM `$REPO_ROOT/templates/tools/distrib/python/grpcio_tools/grpc_version.py.template`!!!
+
+  VERSION = '${settings.python_version.pep440()}'
diff --git a/tools/distrib/grpc_version.py b/tools/distrib/grpc_version.py
new file mode 100644
index 00000000000..3f85118019d
--- /dev/null
+++ b/tools/distrib/grpc_version.py
@@ -0,0 +1,17 @@
+# Copyright 2020 gRPC authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# AUTO-GENERATED FROM `$REPO_ROOT/templates/tools/distrib/python/grpcio_tools/grpc_version.py.template`!!!
+
+VERSION = '1.32.0.dev0'
diff --git a/tools/distrib/python/docgen.py b/tools/distrib/python/docgen.py
index 9c9e5910db7..9cbe56b2ebd 100755
--- a/tools/distrib/python/docgen.py
+++ b/tools/distrib/python/docgen.py
@@ -22,77 +22,52 @@ import shutil
 import subprocess
 import sys
 import tempfile
+import grpc_version
 
 parser = argparse.ArgumentParser()
-parser.add_argument('--config',
-                    metavar='c',
-                    type=str,
-                    nargs=1,
-                    help='GRPC/GPR libraries build configuration',
-                    default='opt')
-parser.add_argument('--submit', action='store_true')
 parser.add_argument('--repo-owner',
                     type=str,
                     help=('Owner of the GitHub repository to be pushed'))
-parser.add_argument('--doc-branch', type=str)
+parser.add_argument('--doc-branch',
+                    type=str,
+                    default='python-doc-%s' % grpc_version.VERSION)
 args = parser.parse_args()
 
 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
 PROJECT_ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, '..', '..', '..'))
 
-CONFIG = args.config
 SETUP_PATH = os.path.join(PROJECT_ROOT, 'setup.py')
 REQUIREMENTS_PATH = os.path.join(PROJECT_ROOT, 'requirements.bazel.txt')
 DOC_PATH = os.path.join(PROJECT_ROOT, 'doc/build')
-INCLUDE_PATH = os.path.join(PROJECT_ROOT, 'include')
-LIBRARY_PATH = os.path.join(PROJECT_ROOT, 'libs/{}'.format(CONFIG))
-VIRTUALENV_DIR = os.path.join(SCRIPT_DIR, 'distrib_virtualenv')
-VIRTUALENV_PYTHON_PATH = os.path.join(VIRTUALENV_DIR, 'bin', 'python')
-VIRTUALENV_PIP_PATH = os.path.join(VIRTUALENV_DIR, 'bin', 'pip')
 
-environment = os.environ.copy()
-environment.update({
-    'CONFIG': CONFIG,
-    'CFLAGS': '-I{}'.format(INCLUDE_PATH),
-    'LDFLAGS': '-L{}'.format(LIBRARY_PATH),
-    'LD_LIBRARY_PATH': LIBRARY_PATH,
-    'GRPC_PYTHON_BUILD_WITH_CYTHON': '1',
-    'GRPC_PYTHON_ENABLE_DOCUMENTATION_BUILD': '1',
-})
+if "VIRTUAL_ENV" in os.environ:
+    VIRTUALENV_DIR = ''
+    PYTHON_PATH = sys.executable
+    subprocess_arguments_list = []
+else:
+    VIRTUALENV_DIR = os.path.join(SCRIPT_DIR, 'distrib_virtualenv')
+    PYTHON_PATH = os.path.join(VIRTUALENV_DIR, 'bin', 'python')
+    subprocess_arguments_list = [
+        ['python3', '-m', 'virtualenv', VIRTUALENV_DIR],
+    ]
 
-subprocess_arguments_list = [
-    {
-        'args': ['virtualenv', VIRTUALENV_DIR],
-        'env': environment
-    },
-    {
-        'args': [VIRTUALENV_PIP_PATH, 'install', '--upgrade', 'pip==19.3.1'],
-        'env': environment
-    },
-    {
-        'args': [VIRTUALENV_PIP_PATH, 'install', '-r', REQUIREMENTS_PATH],
-        'env': environment
-    },
-    {
-        'args': [VIRTUALENV_PIP_PATH, 'install', 'Sphinx~=1.8.1'],
-        'env': environment
-    },
-    {
-        'args': [VIRTUALENV_PYTHON_PATH, SETUP_PATH, 'doc'],
-        'env': environment
-    },
+subprocess_arguments_list += [
+    [PYTHON_PATH, '-m', 'pip', 'install', '--upgrade', 'pip==19.3.1'],
+    [PYTHON_PATH, '-m', 'pip', 'install', '-r', REQUIREMENTS_PATH],
+    [PYTHON_PATH, '-m', 'pip', 'install', '--upgrade', 'Sphinx'],
+    [PYTHON_PATH, SETUP_PATH, 'doc'],
 ]
 
 for subprocess_arguments in subprocess_arguments_list:
-    print('Running command: {}'.format(subprocess_arguments['args']))
-    subprocess.check_call(**subprocess_arguments)
+    print('Running command: {}'.format(subprocess_arguments))
+    subprocess.check_call(args=subprocess_arguments)
 
-if not args.submit:
+if not args.repo_owner or not args.doc_branch:
     print('Please check generated Python doc inside doc/build')
-elif args.submit:
-    assert args.repo_owner
-    assert args.doc_branch
-    github_repository_owner = args.repo_owner
+    print(
+        'To push to a GitHub repo, please provide repo owner and doc branch name'
+    )
+else:
     # Create a temporary directory out of tree, checkout gh-pages from the
     # specified repository, edit it, and push it. It's up to the user to then go
     # onto GitHub and make a PR against grpc/grpc:gh-pages.
@@ -114,13 +89,14 @@ elif args.submit:
     subprocess.check_call(['git', 'checkout', '-b', doc_branch], cwd=repo_dir)
     subprocess.check_call([
         'git', 'remote', 'add', 'ssh-origin',
-        'git@github.com:%s/grpc.git' % (github_repository_owner)
+        'git@github.com:%s/grpc.git' % (args.repo_owner)
     ],
                           cwd=repo_dir)
     print('Updating documentation...')
     shutil.rmtree(python_doc_dir, ignore_errors=True)
     shutil.copytree(DOC_PATH, python_doc_dir)
-    print('Attempting to push documentation...')
+    print('Attempting to push documentation to %s/%s...' %
+          (args.repo_owner, doc_branch))
     try:
         subprocess.check_call(['git', 'add', '--all'], cwd=repo_dir)
         subprocess.check_call(
diff --git a/tools/distrib/python/grpc_version.py b/tools/distrib/python/grpc_version.py
new file mode 100644
index 00000000000..3f85118019d
--- /dev/null
+++ b/tools/distrib/python/grpc_version.py
@@ -0,0 +1,17 @@
+# Copyright 2020 gRPC authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# AUTO-GENERATED FROM `$REPO_ROOT/templates/tools/distrib/python/grpcio_tools/grpc_version.py.template`!!!
+
+VERSION = '1.32.0.dev0'

From 17b207ae93a04b02339882e0e846e468c827b411 Mon Sep 17 00:00:00 2001
From: Karthik Ravi Shankar <karthikrs@google.com>
Date: Mon, 3 Aug 2020 11:30:24 -0700
Subject: [PATCH 31/38] Fix build_autoregenerated.yaml

---
 build_autogenerated.yaml | 1 -
 1 file changed, 1 deletion(-)

diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml
index 115097a3749..9bd597bad31 100644
--- a/build_autogenerated.yaml
+++ b/build_autogenerated.yaml
@@ -2276,7 +2276,6 @@ libs:
   public_headers:
   - include/grpc++/support/error_details.h
   - include/grpcpp/support/error_details.h
-  - include/grpcpp/support/error_details_impl.h
   headers: []
   src:
   - src/proto/grpc/status/status.proto

From 38530cc2bc08edf45f1cb3eb91bcd2f806141ee8 Mon Sep 17 00:00:00 2001
From: Lidi Zheng <lidiz@google.com>
Date: Mon, 3 Aug 2020 11:44:16 -0700
Subject: [PATCH 32/38] Make docgen work better with virtualenv

---
 tools/distrib/python/docgen.py | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/tools/distrib/python/docgen.py b/tools/distrib/python/docgen.py
index 9cbe56b2ebd..0fc49a1d21c 100755
--- a/tools/distrib/python/docgen.py
+++ b/tools/distrib/python/docgen.py
@@ -41,8 +41,8 @@ REQUIREMENTS_PATH = os.path.join(PROJECT_ROOT, 'requirements.bazel.txt')
 DOC_PATH = os.path.join(PROJECT_ROOT, 'doc/build')
 
 if "VIRTUAL_ENV" in os.environ:
-    VIRTUALENV_DIR = ''
-    PYTHON_PATH = sys.executable
+    VIRTUALENV_DIR = os.environ['VIRTUAL_ENV']
+    PYTHON_PATH = os.path.join(VIRTUALENV_DIR, 'bin', 'python')
     subprocess_arguments_list = []
 else:
     VIRTUALENV_DIR = os.path.join(SCRIPT_DIR, 'distrib_virtualenv')
@@ -63,6 +63,8 @@ for subprocess_arguments in subprocess_arguments_list:
     subprocess.check_call(args=subprocess_arguments)
 
 if not args.repo_owner or not args.doc_branch:
+    tty_width = int(os.popen('stty size', 'r').read().split()[1])
+    print('-' * tty_width)
     print('Please check generated Python doc inside doc/build')
     print(
         'To push to a GitHub repo, please provide repo owner and doc branch name'

From 5151783c580996b519f9599962445fafa24c595f Mon Sep 17 00:00:00 2001
From: Lidi Zheng <lidiz@google.com>
Date: Mon, 3 Aug 2020 11:52:48 -0700
Subject: [PATCH 33/38] Remove unused file

---
 tools/distrib/grpc_version.py | 17 -----------------
 1 file changed, 17 deletions(-)
 delete mode 100644 tools/distrib/grpc_version.py

diff --git a/tools/distrib/grpc_version.py b/tools/distrib/grpc_version.py
deleted file mode 100644
index 3f85118019d..00000000000
--- a/tools/distrib/grpc_version.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright 2020 gRPC authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# AUTO-GENERATED FROM `$REPO_ROOT/templates/tools/distrib/python/grpcio_tools/grpc_version.py.template`!!!
-
-VERSION = '1.32.0.dev0'

From 0b61aea1c521990f3fd0d2e11649fb14a740b1ae Mon Sep 17 00:00:00 2001
From: "Mark D. Roth" <roth@google.com>
Date: Mon, 3 Aug 2020 12:56:29 -0700
Subject: [PATCH 34/38] Convert grpc_server to idiomatic C++.

---
 .../transport/chttp2/server/chttp2_server.cc  |   44 +-
 .../transport/chttp2/server/chttp2_server.h   |    3 +-
 .../chttp2/server/insecure/server_chttp2.cc   |    4 +-
 .../server/insecure/server_chttp2_posix.cc    |   12 +-
 .../server/secure/server_secure_chttp2.cc     |    5 +-
 .../ext/transport/inproc/inproc_transport.cc  |    6 +-
 src/core/lib/channel/channelz.cc              |    2 +-
 src/core/lib/channel/channelz.h               |    2 +-
 src/core/lib/surface/call.cc                  |   24 +-
 src/core/lib/surface/call.h                   |    3 +-
 src/core/lib/surface/init.cc                  |    2 +-
 src/core/lib/surface/server.cc                | 2308 ++++++++---------
 src/core/lib/surface/server.h                 |  450 +++-
 src/cpp/server/server_cc.cc                   |   14 +-
 test/core/bad_client/bad_client.cc            |    6 +-
 .../core/bad_client/tests/bad_streaming_id.cc |    2 +-
 test/core/bad_client/tests/badreq.cc          |    2 +-
 .../bad_client/tests/connection_prefix.cc     |    2 +-
 test/core/bad_client/tests/headers.cc         |    2 +-
 .../tests/initial_settings_frame.cc           |    2 +-
 test/core/bad_client/tests/out_of_bounds.cc   |    2 +-
 .../tests/server_registered_method.cc         |    2 +-
 test/core/bad_client/tests/simple_request.cc  |    2 +-
 test/core/bad_client/tests/unknown_frame.cc   |    2 +-
 test/core/bad_client/tests/window_overflow.cc |    2 +-
 test/core/bad_connection/close_fd_test.cc     |    5 +-
 test/core/channel/channelz_test.cc            |    2 +-
 .../end2end/fixtures/h2_sockpair+trace.cc     |    4 +-
 test/core/end2end/fixtures/h2_sockpair.cc     |    4 +-
 .../end2end/fixtures/h2_sockpair_1byte.cc     |    4 +-
 test/core/end2end/fuzzers/server_fuzzer.cc    |    2 +-
 test/core/end2end/tests/channelz.cc           |    4 +-
 test/cpp/microbenchmarks/fullstack_fixtures.h |    8 +-
 test/cpp/performance/writes_per_rpc_test.cc   |    8 +-
 34 files changed, 1519 insertions(+), 1427 deletions(-)

diff --git a/src/core/ext/transport/chttp2/server/chttp2_server.cc b/src/core/ext/transport/chttp2/server/chttp2_server.cc
index 6bafa823e06..000c63c8786 100644
--- a/src/core/ext/transport/chttp2/server/chttp2_server.cc
+++ b/src/core/ext/transport/chttp2/server/chttp2_server.cc
@@ -53,19 +53,19 @@
 namespace grpc_core {
 namespace {
 
-class Chttp2ServerListener : public ServerListenerInterface {
+class Chttp2ServerListener : public Server::ListenerInterface {
  public:
-  static grpc_error* Create(grpc_server* server, const char* addr,
+  static grpc_error* Create(Server* server, const char* addr,
                             grpc_channel_args* args, int* port_num);
 
-  static grpc_error* CreateWithAcceptor(grpc_server* server, const char* name,
+  static grpc_error* CreateWithAcceptor(Server* server, const char* name,
                                         grpc_channel_args* args);
 
   // Do not instantiate directly.  Use one of the factory methods above.
-  Chttp2ServerListener(grpc_server* server, grpc_channel_args* args);
+  Chttp2ServerListener(Server* server, grpc_channel_args* args);
   ~Chttp2ServerListener();
 
-  void Start(grpc_server* server,
+  void Start(Server* server,
              const std::vector<grpc_pollset*>* pollsets) override;
 
   channelz::ListenSocketNode* channelz_listen_socket_node() const override {
@@ -113,10 +113,10 @@ class Chttp2ServerListener : public ServerListenerInterface {
 
   static void TcpServerShutdownComplete(void* arg, grpc_error* error);
 
-  static void DestroyListener(grpc_server* /*server*/, void* arg,
+  static void DestroyListener(Server* /*server*/, void* arg,
                               grpc_closure* destroy_done);
 
-  grpc_server* const server_;
+  Server* const server_;
   grpc_channel_args* const args_;
   grpc_tcp_server* tcp_server_;
   Mutex mu_;
@@ -194,12 +194,10 @@ void Chttp2ServerListener::ConnectionState::OnHandshakeDone(void* arg,
   {
     MutexLock lock(&self->listener_->mu_);
     grpc_resource_user* resource_user =
-        grpc_server_get_default_resource_user(self->listener_->server_);
+        self->listener_->server_->default_resource_user();
     if (error != GRPC_ERROR_NONE || self->listener_->shutdown_) {
       const char* error_str = grpc_error_string(error);
       gpr_log(GPR_DEBUG, "Handshaking failed: %s", error_str);
-      grpc_resource_user* resource_user =
-          grpc_server_get_default_resource_user(self->listener_->server_);
       if (resource_user != nullptr) {
         grpc_resource_user_free(resource_user,
                                 GRPC_RESOURCE_QUOTA_CHANNEL_SIZE);
@@ -224,10 +222,9 @@ void Chttp2ServerListener::ConnectionState::OnHandshakeDone(void* arg,
       if (args->endpoint != nullptr) {
         grpc_transport* transport = grpc_create_chttp2_transport(
             args->args, args->endpoint, false, resource_user);
-        grpc_server_setup_transport(
-            self->listener_->server_, transport, self->accepting_pollset_,
-            args->args, grpc_chttp2_transport_get_socket_node(transport),
-            resource_user);
+        self->listener_->server_->SetupTransport(
+            transport, self->accepting_pollset_, args->args,
+            grpc_chttp2_transport_get_socket_node(transport), resource_user);
         // Use notify_on_receive_settings callback to enforce the
         // handshake deadline.
         // Note: The reinterpret_cast<>s here are safe, because
@@ -270,7 +267,7 @@ void Chttp2ServerListener::ConnectionState::OnHandshakeDone(void* arg,
 // Chttp2ServerListener
 //
 
-grpc_error* Chttp2ServerListener::Create(grpc_server* server, const char* addr,
+grpc_error* Chttp2ServerListener::Create(Server* server, const char* addr,
                                          grpc_channel_args* args,
                                          int* port_num) {
   std::vector<grpc_error*> error_list;
@@ -327,8 +324,7 @@ grpc_error* Chttp2ServerListener::Create(grpc_server* server, const char* addr,
               addr, absl::StrFormat("chttp2 listener %s", addr));
     }
     /* Register with the server only upon success */
-    grpc_server_add_listener(server,
-                             OrphanablePtr<ServerListenerInterface>(listener));
+    server->AddListener(OrphanablePtr<Server::ListenerInterface>(listener));
     return GRPC_ERROR_NONE;
   }();
   if (resolved != nullptr) {
@@ -352,7 +348,7 @@ grpc_error* Chttp2ServerListener::Create(grpc_server* server, const char* addr,
   return error;
 }
 
-grpc_error* Chttp2ServerListener::CreateWithAcceptor(grpc_server* server,
+grpc_error* Chttp2ServerListener::CreateWithAcceptor(Server* server,
                                                      const char* name,
                                                      grpc_channel_args* args) {
   Chttp2ServerListener* listener = new Chttp2ServerListener(server, args);
@@ -366,12 +362,11 @@ grpc_error* Chttp2ServerListener::CreateWithAcceptor(grpc_server* server,
   TcpServerFdHandler** arg_val =
       grpc_channel_args_find_pointer<TcpServerFdHandler*>(args, name);
   *arg_val = grpc_tcp_server_create_fd_handler(listener->tcp_server_);
-  grpc_server_add_listener(server,
-                           OrphanablePtr<ServerListenerInterface>(listener));
+  server->AddListener(OrphanablePtr<Server::ListenerInterface>(listener));
   return GRPC_ERROR_NONE;
 }
 
-Chttp2ServerListener::Chttp2ServerListener(grpc_server* server,
+Chttp2ServerListener::Chttp2ServerListener(Server* server,
                                            grpc_channel_args* args)
     : server_(server), args_(args) {
   GRPC_CLOSURE_INIT(&tcp_server_shutdown_complete_, TcpServerShutdownComplete,
@@ -383,7 +378,7 @@ Chttp2ServerListener::~Chttp2ServerListener() {
 }
 
 /* Server callback: start listening on our ports */
-void Chttp2ServerListener::Start(grpc_server* /*server*/,
+void Chttp2ServerListener::Start(Server* /*server*/,
                                  const std::vector<grpc_pollset*>* pollsets) {
   {
     MutexLock lock(&mu_);
@@ -400,8 +395,7 @@ void Chttp2ServerListener::SetOnDestroyDone(grpc_closure* on_destroy_done) {
 RefCountedPtr<HandshakeManager> Chttp2ServerListener::CreateHandshakeManager() {
   MutexLock lock(&mu_);
   if (shutdown_) return nullptr;
-  grpc_resource_user* resource_user =
-      grpc_server_get_default_resource_user(server_);
+  grpc_resource_user* resource_user = server_->default_resource_user();
   if (resource_user != nullptr &&
       !grpc_resource_user_safe_alloc(resource_user,
                                      GRPC_RESOURCE_QUOTA_CHANNEL_SIZE)) {
@@ -475,7 +469,7 @@ void Chttp2ServerListener::Orphan() {
 // Chttp2ServerAddPort()
 //
 
-grpc_error* Chttp2ServerAddPort(grpc_server* server, const char* addr,
+grpc_error* Chttp2ServerAddPort(Server* server, const char* addr,
                                 grpc_channel_args* args, int* port_num) {
   if (strncmp(addr, "external:", 9) == 0) {
     return grpc_core::Chttp2ServerListener::CreateWithAcceptor(server, addr,
diff --git a/src/core/ext/transport/chttp2/server/chttp2_server.h b/src/core/ext/transport/chttp2/server/chttp2_server.h
index 8f651bb5ce9..095cc5e4c04 100644
--- a/src/core/ext/transport/chttp2/server/chttp2_server.h
+++ b/src/core/ext/transport/chttp2/server/chttp2_server.h
@@ -24,12 +24,13 @@
 #include <grpc/impl/codegen/grpc_types.h>
 
 #include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/surface/server.h"
 
 namespace grpc_core {
 
 /// Adds a port to \a server.  Sets \a port_num to the port number.
 /// Takes ownership of \a args.
-grpc_error* Chttp2ServerAddPort(grpc_server* server, const char* addr,
+grpc_error* Chttp2ServerAddPort(Server* server, const char* addr,
                                 grpc_channel_args* args, int* port_num);
 
 }  // namespace grpc_core
diff --git a/src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc b/src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc
index 7220591a30f..eebae9efaa9 100644
--- a/src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc
+++ b/src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc
@@ -33,8 +33,8 @@ int grpc_server_add_insecure_http2_port(grpc_server* server, const char* addr) {
   GRPC_API_TRACE("grpc_server_add_insecure_http2_port(server=%p, addr=%s)", 2,
                  (server, addr));
   grpc_error* err = grpc_core::Chttp2ServerAddPort(
-      server, addr,
-      grpc_channel_args_copy(grpc_server_get_channel_args(server)), &port_num);
+      server->core_server.get(), addr,
+      grpc_channel_args_copy(server->core_server->channel_args()), &port_num);
   if (err != GRPC_ERROR_NONE) {
     const char* msg = grpc_error_string(err);
     gpr_log(GPR_ERROR, "%s", msg);
diff --git a/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc b/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc
index f3ae7f4edb7..f27c197ea15 100644
--- a/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc
+++ b/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc
@@ -41,21 +41,21 @@ void grpc_server_add_insecure_channel_from_fd(grpc_server* server,
   GPR_ASSERT(reserved == nullptr);
 
   grpc_core::ExecCtx exec_ctx;
+  grpc_core::Server* core_server = server->core_server.get();
 
+  const grpc_channel_args* server_args = core_server->channel_args();
   std::string name = absl::StrCat("fd:", fd);
-  grpc_endpoint* server_endpoint =
-      grpc_tcp_create(grpc_fd_create(fd, name.c_str(), true),
-                      grpc_server_get_channel_args(server), name.c_str());
+  grpc_endpoint* server_endpoint = grpc_tcp_create(
+      grpc_fd_create(fd, name.c_str(), true), server_args, name.c_str());
 
-  const grpc_channel_args* server_args = grpc_server_get_channel_args(server);
   grpc_transport* transport = grpc_create_chttp2_transport(
       server_args, server_endpoint, false /* is_client */);
 
-  for (grpc_pollset* pollset : grpc_server_get_pollsets(server)) {
+  for (grpc_pollset* pollset : core_server->pollsets()) {
     grpc_endpoint_add_to_pollset(server_endpoint, pollset);
   }
 
-  grpc_server_setup_transport(server, transport, nullptr, server_args, nullptr);
+  core_server->SetupTransport(transport, nullptr, server_args, nullptr);
   grpc_chttp2_transport_start_reading(transport, nullptr, nullptr);
 }
 
diff --git a/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc b/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc
index c3c4e39033b..a181db47f39 100644
--- a/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc
+++ b/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc
@@ -68,10 +68,11 @@ int grpc_server_add_secure_http2_port(grpc_server* server, const char* addr,
   args_to_add[0] = grpc_server_credentials_to_arg(creds);
   args_to_add[1] = grpc_security_connector_to_arg(sc.get());
   args =
-      grpc_channel_args_copy_and_add(grpc_server_get_channel_args(server),
+      grpc_channel_args_copy_and_add(server->core_server->channel_args(),
                                      args_to_add, GPR_ARRAY_SIZE(args_to_add));
   // Add server port.
-  err = grpc_core::Chttp2ServerAddPort(server, addr, args, &port_num);
+  err = grpc_core::Chttp2ServerAddPort(server->core_server.get(), addr, args,
+                                       &port_num);
 done:
   sc.reset(DEBUG_LOCATION, "server");
 
diff --git a/src/core/ext/transport/inproc/inproc_transport.cc b/src/core/ext/transport/inproc/inproc_transport.cc
index d40bffd6d09..4cf9e900a44 100644
--- a/src/core/ext/transport/inproc/inproc_transport.cc
+++ b/src/core/ext/transport/inproc/inproc_transport.cc
@@ -1276,7 +1276,7 @@ grpc_channel* grpc_inproc_channel_create(grpc_server* server,
   const char* args_to_remove[] = {GRPC_ARG_MAX_CONNECTION_IDLE_MS,
                                   GRPC_ARG_MAX_CONNECTION_AGE_MS};
   const grpc_channel_args* server_args = grpc_channel_args_copy_and_remove(
-      grpc_server_get_channel_args(server), args_to_remove,
+      server->core_server->channel_args(), args_to_remove,
       GPR_ARRAY_SIZE(args_to_remove));
 
   // Add a default authority channel argument for the client
@@ -1293,8 +1293,8 @@ grpc_channel* grpc_inproc_channel_create(grpc_server* server,
                            client_args);
 
   // TODO(ncteisen): design and support channelz GetSocket for inproc.
-  grpc_server_setup_transport(server, server_transport, nullptr, server_args,
-                              nullptr);
+  server->core_server->SetupTransport(server_transport, nullptr, server_args,
+                                      nullptr);
   grpc_channel* channel = grpc_channel_create(
       "inproc", client_args, GRPC_CLIENT_DIRECT_CHANNEL, client_transport);
 
diff --git a/src/core/lib/channel/channelz.cc b/src/core/lib/channel/channelz.cc
index a79a0147be3..64748fd854e 100644
--- a/src/core/lib/channel/channelz.cc
+++ b/src/core/lib/channel/channelz.cc
@@ -283,7 +283,7 @@ void ChannelNode::RemoveChildSubchannel(intptr_t child_uuid) {
 // ServerNode
 //
 
-ServerNode::ServerNode(grpc_server* /*server*/, size_t channel_tracer_max_nodes)
+ServerNode::ServerNode(size_t channel_tracer_max_nodes)
     : BaseNode(EntityType::kServer, ""), trace_(channel_tracer_max_nodes) {}
 
 ServerNode::~ServerNode() {}
diff --git a/src/core/lib/channel/channelz.h b/src/core/lib/channel/channelz.h
index 63502030966..9a19beccbaf 100644
--- a/src/core/lib/channel/channelz.h
+++ b/src/core/lib/channel/channelz.h
@@ -238,7 +238,7 @@ class ChannelNode : public BaseNode {
 // Handles channelz bookkeeping for servers
 class ServerNode : public BaseNode {
  public:
-  ServerNode(grpc_server* server, size_t channel_tracer_max_nodes);
+  explicit ServerNode(size_t channel_tracer_max_nodes);
 
   ~ServerNode() override;
 
diff --git a/src/core/lib/surface/call.cc b/src/core/lib/surface/call.cc
index 31f1559d72d..becb23518e2 100644
--- a/src/core/lib/surface/call.cc
+++ b/src/core/lib/surface/call.cc
@@ -245,7 +245,7 @@ struct grpc_call {
     struct {
       int* cancelled;
       // backpointer to owning server if this is a server side call.
-      grpc_server* server;
+      grpc_core::Server* core_server;
     } server;
   } final_op;
   gpr_atm status_error = 0;
@@ -374,7 +374,7 @@ grpc_error* grpc_call_create(const grpc_call_create_args* args,
   } else {
     GRPC_STATS_INC_SERVER_CALLS_CREATED();
     call->final_op.server.cancelled = nullptr;
-    call->final_op.server.server = args->server;
+    call->final_op.server.core_server = args->server;
     GPR_ASSERT(args->add_initial_metadata_count == 0);
     call->send_extra_metadata_count = 0;
   }
@@ -476,11 +476,11 @@ grpc_error* grpc_call_create(const grpc_call_create_args* args,
     if (channelz_channel != nullptr) {
       channelz_channel->RecordCallStarted();
     }
-  } else {
-    grpc_core::channelz::ServerNode* channelz_server =
-        grpc_server_get_channelz_node(call->final_op.server.server);
-    if (channelz_server != nullptr) {
-      channelz_server->RecordCallStarted();
+  } else if (call->final_op.server.core_server != nullptr) {
+    grpc_core::channelz::ServerNode* channelz_node =
+        call->final_op.server.core_server->channelz_node();
+    if (channelz_node != nullptr) {
+      channelz_node->RecordCallStarted();
     }
   }
 
@@ -759,15 +759,15 @@ static void set_final_status(grpc_call* call, grpc_error* error) {
   } else {
     *call->final_op.server.cancelled =
         error != GRPC_ERROR_NONE || !call->sent_server_trailing_metadata;
-    grpc_core::channelz::ServerNode* channelz_server =
-        grpc_server_get_channelz_node(call->final_op.server.server);
-    if (channelz_server != nullptr) {
+    grpc_core::channelz::ServerNode* channelz_node =
+        call->final_op.server.core_server->channelz_node();
+    if (channelz_node != nullptr) {
       if (*call->final_op.server.cancelled ||
           reinterpret_cast<grpc_error*>(
               gpr_atm_acq_load(&call->status_error)) != GRPC_ERROR_NONE) {
-        channelz_server->RecordCallFailed();
+        channelz_node->RecordCallFailed();
       } else {
-        channelz_server->RecordCallSucceeded();
+        channelz_node->RecordCallSucceeded();
       }
     }
     GRPC_ERROR_UNREF(error);
diff --git a/src/core/lib/surface/call.h b/src/core/lib/surface/call.h
index a33664af6a9..34d01db9029 100644
--- a/src/core/lib/surface/call.h
+++ b/src/core/lib/surface/call.h
@@ -25,6 +25,7 @@
 #include "src/core/lib/channel/context.h"
 #include "src/core/lib/gprpp/arena.h"
 #include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/server.h"
 
 #include <grpc/grpc.h>
 #include <grpc/impl/codegen/compression_types.h>
@@ -34,7 +35,7 @@ typedef void (*grpc_ioreq_completion_func)(grpc_call* call, int success,
 
 typedef struct grpc_call_create_args {
   grpc_channel* channel;
-  grpc_server* server;
+  grpc_core::Server* server;
 
   grpc_call* parent;
   uint32_t propagation_mask;
diff --git a/src/core/lib/surface/init.cc b/src/core/lib/surface/init.cc
index d8bc4e4dd32..88a69495c01 100644
--- a/src/core/lib/surface/init.cc
+++ b/src/core/lib/surface/init.cc
@@ -102,7 +102,7 @@ static void register_builtin_channel_init() {
                                    GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
                                    append_filter, (void*)&grpc_lame_filter);
   grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, INT_MAX, prepend_filter,
-                                   (void*)&grpc_server_top_filter);
+                                   (void*)&grpc_core::Server::kServerTopFilter);
 }
 
 typedef struct grpc_plugin {
diff --git a/src/core/lib/surface/server.cc b/src/core/lib/surface/server.cc
index 9b7c2262137..d66896a5a88 100644
--- a/src/core/lib/surface/server.cc
+++ b/src/core/lib/surface/server.cc
@@ -1,20 +1,18 @@
-/*
- *
- * Copyright 2015-2016 gRPC authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
+//
+// Copyright 2015-2016 gRPC authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
 
 #include <grpc/support/port_platform.h>
 
@@ -28,15 +26,16 @@
 #include <atomic>
 #include <iterator>
 #include <list>
+#include <queue>
 #include <utility>
 #include <vector>
 
+#include "absl/types/optional.h"
+
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 
-#include "absl/types/optional.h"
-
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/channel/channelz.h"
 #include "src/core/lib/channel/connected_channel.h"
@@ -59,28 +58,17 @@ namespace grpc_core {
 
 TraceFlag grpc_server_channel_trace(false, "server_channel");
 
-namespace {
-
-void server_on_recv_initial_metadata(void* ptr, grpc_error* error);
-void server_recv_trailing_metadata_ready(void* user_data, grpc_error* error);
-
-struct Listener {
-  explicit Listener(OrphanablePtr<ServerListenerInterface> l)
-      : listener(std::move(l)) {}
-
-  OrphanablePtr<ServerListenerInterface> listener;
-  grpc_closure destroy_done;
-};
-
-enum class RequestedCallType { BATCH_CALL, REGISTERED_CALL };
+//
+// Server::RequestedCall
+//
 
-struct registered_method;
+struct Server::RequestedCall {
+  enum class Type { BATCH_CALL, REGISTERED_CALL };
 
-struct requested_call {
-  requested_call(void* tag_arg, grpc_completion_queue* call_cq,
-                 grpc_call** call_arg, grpc_metadata_array* initial_md,
-                 grpc_call_details* details)
-      : type(RequestedCallType::BATCH_CALL),
+  RequestedCall(void* tag_arg, grpc_completion_queue* call_cq,
+                grpc_call** call_arg, grpc_metadata_array* initial_md,
+                grpc_call_details* details)
+      : type(Type::BATCH_CALL),
         tag(tag_arg),
         cq_bound_to_call(call_cq),
         call(call_arg),
@@ -89,11 +77,11 @@ struct requested_call {
     data.batch.details = details;
   }
 
-  requested_call(void* tag_arg, grpc_completion_queue* call_cq,
-                 grpc_call** call_arg, grpc_metadata_array* initial_md,
-                 registered_method* rm, gpr_timespec* deadline,
-                 grpc_byte_buffer** optional_payload)
-      : type(RequestedCallType::REGISTERED_CALL),
+  RequestedCall(void* tag_arg, grpc_completion_queue* call_cq,
+                grpc_call** call_arg, grpc_metadata_array* initial_md,
+                RegisteredMethod* rm, gpr_timespec* deadline,
+                grpc_byte_buffer** optional_payload)
+      : type(Type::REGISTERED_CALL),
         tag(tag_arg),
         cq_bound_to_call(call_cq),
         call(call_arg),
@@ -104,7 +92,7 @@ struct requested_call {
   }
 
   MultiProducerSingleConsumerQueue::Node mpscq_node;
-  const RequestedCallType type;
+  const Type type;
   void* const tag;
   grpc_completion_queue* const cq_bound_to_call;
   grpc_call** const call;
@@ -115,66 +103,40 @@ struct requested_call {
       grpc_call_details* details;
     } batch;
     struct {
-      registered_method* method;
+      RegisteredMethod* method;
       gpr_timespec* deadline;
       grpc_byte_buffer** optional_payload;
     } registered;
   } data;
 };
 
-struct channel_registered_method {
-  registered_method* server_registered_method = nullptr;
-  uint32_t flags;
-  bool has_host;
-  ExternallyManagedSlice method;
-  ExternallyManagedSlice host;
-};
-
-struct channel_data {
-  channel_data() = default;
-  ~channel_data();
-
-  grpc_server* server = nullptr;
-  grpc_channel* channel;
-  size_t cq_idx;
-  absl::optional<std::list<channel_data*>::iterator> list_position;
-
-  // registered_methods is a hash-table of the methods and hosts of the
-  // registered methods.
-  // TODO(vjpai): Convert this to an STL map type as opposed to a direct bucket
-  // implementation. (Consider performance impact, hash function to use, etc.)
-  std::unique_ptr<std::vector<channel_registered_method>> registered_methods;
-  uint32_t registered_method_max_probes;
-
-  grpc_closure finish_destroy_channel_closure;
-  intptr_t channelz_socket_uuid;
-};
+//
+// Server::RegisteredMethod
+//
 
-struct shutdown_tag {
-  shutdown_tag(void* tag_arg, grpc_completion_queue* cq_arg)
-      : tag(tag_arg), cq(cq_arg) {}
+struct Server::RegisteredMethod {
+  RegisteredMethod(
+      const char* method_arg, const char* host_arg,
+      grpc_server_register_method_payload_handling payload_handling_arg,
+      uint32_t flags_arg)
+      : method(method_arg == nullptr ? "" : method_arg),
+        host(host_arg == nullptr ? "" : host_arg),
+        payload_handling(payload_handling_arg),
+        flags(flags_arg) {}
 
-  void* const tag;
-  grpc_completion_queue* const cq;
-  grpc_cq_completion completion;
-};
+  ~RegisteredMethod() = default;
 
-enum class CallState {
-  /* waiting for metadata */
-  NOT_STARTED,
-  /* initial metadata read, not flow controlled in yet */
-  PENDING,
-  /* flow controlled in, on completion queue */
-  ACTIVATED,
-  /* cancelled before being queued */
-  ZOMBIED
+  const std::string method;
+  const std::string host;
+  const grpc_server_register_method_payload_handling payload_handling;
+  const uint32_t flags;
+  // One request matcher per method.
+  std::unique_ptr<RequestMatcherInterface> matcher;
 };
 
-struct call_data;
-
-grpc_call_error ValidateServerRequest(
-    grpc_completion_queue* cq_for_notification, void* tag,
-    grpc_byte_buffer** optional_payload, registered_method* rm);
+//
+// Server::RequestMatcherInterface
+//
 
 // RPCs that come in from the transport must be matched against RPC requests
 // from the application. An incoming request from the application can be matched
@@ -185,7 +147,7 @@ grpc_call_error ValidateServerRequest(
 // on the request's notification CQ.
 //
 // RequestMatcherInterface is the base class to provide this functionality.
-class RequestMatcherInterface {
+class Server::RequestMatcherInterface {
  public:
   virtual ~RequestMatcherInterface() {}
 
@@ -208,7 +170,7 @@ class RequestMatcherInterface {
   // CQ. If there are pending RPCs waiting to be matched, publish one (match it
   // and notify the CQ).
   virtual void RequestCallWithPossiblePublish(size_t request_queue_index,
-                                              requested_call* call) = 0;
+                                              RequestedCall* call) = 0;
 
   // This function is invoked on an incoming RPC, represented by the calld
   // object. The RequestMatcher will try to match it against an
@@ -217,339 +179,21 @@ class RequestMatcherInterface {
   // is done starting at the start_request_queue_index parameter in a cyclic
   // order rather than always starting at 0.
   virtual void MatchOrQueue(size_t start_request_queue_index,
-                            call_data* calld) = 0;
+                            CallData* calld) = 0;
 
   // Returns the server associated with this request matcher
-  virtual grpc_server* server() const = 0;
-};
-
-struct call_data {
-  call_data(grpc_call_element* elem, const grpc_call_element_args& args)
-      : call(grpc_call_from_top_element(elem)),
-        call_combiner(args.call_combiner) {
-    GRPC_CLOSURE_INIT(&on_recv_initial_metadata,
-                      server_on_recv_initial_metadata, elem,
-                      grpc_schedule_on_exec_ctx);
-    GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready,
-                      server_recv_trailing_metadata_ready, elem,
-                      grpc_schedule_on_exec_ctx);
-  }
-  ~call_data() {
-    GPR_ASSERT(state.Load(grpc_core::MemoryOrder::RELAXED) !=
-               CallState::PENDING);
-    GRPC_ERROR_UNREF(recv_initial_metadata_error);
-    if (host_set) {
-      grpc_slice_unref_internal(host);
-    }
-    if (path_set) {
-      grpc_slice_unref_internal(path);
-    }
-    grpc_metadata_array_destroy(&initial_metadata);
-    grpc_byte_buffer_destroy(payload);
-  }
-
-  grpc_call* call;
-
-  Atomic<CallState> state{CallState::NOT_STARTED};
-
-  bool path_set = false;
-  bool host_set = false;
-  grpc_slice path;
-  grpc_slice host;
-  grpc_millis deadline = GRPC_MILLIS_INF_FUTURE;
-
-  grpc_completion_queue* cq_new = nullptr;
-
-  grpc_metadata_batch* recv_initial_metadata = nullptr;
-  uint32_t recv_initial_metadata_flags = 0;
-  grpc_metadata_array initial_metadata =
-      grpc_metadata_array();  // Zero-initialize the C struct.
-
-  RequestMatcherInterface* matcher = nullptr;
-  grpc_byte_buffer* payload = nullptr;
-
-  grpc_closure got_initial_metadata;
-  grpc_closure on_recv_initial_metadata;
-  grpc_closure kill_zombie_closure;
-  grpc_closure* on_done_recv_initial_metadata;
-  grpc_closure recv_trailing_metadata_ready;
-  grpc_error* recv_initial_metadata_error = GRPC_ERROR_NONE;
-  grpc_closure* original_recv_trailing_metadata_ready;
-  grpc_error* recv_trailing_metadata_error = GRPC_ERROR_NONE;
-  bool seen_recv_trailing_metadata_ready = false;
-
-  grpc_closure publish;
-
-  CallCombiner* call_combiner;
-};
-
-struct registered_method {
-  registered_method(
-      const char* method_arg, const char* host_arg,
-      grpc_server_register_method_payload_handling payload_handling_arg,
-      uint32_t flags_arg)
-      : method(method_arg == nullptr ? "" : method_arg),
-        host(host_arg == nullptr ? "" : host_arg),
-        payload_handling(payload_handling_arg),
-        flags(flags_arg) {}
-
-  ~registered_method() = default;
-
-  const std::string method;
-  const std::string host;
-  const grpc_server_register_method_payload_handling payload_handling;
-  const uint32_t flags;
-  /* one request matcher per method */
-  std::unique_ptr<RequestMatcherInterface> matcher;
-};
-
-}  // namespace
-}  // namespace grpc_core
-
-struct grpc_server {
-  explicit grpc_server(const grpc_channel_args* args)
-      : channel_args(grpc_channel_args_copy(args)) {
-    if (grpc_channel_args_find_bool(args, GRPC_ARG_ENABLE_CHANNELZ,
-                                    GRPC_ENABLE_CHANNELZ_DEFAULT)) {
-      size_t channel_tracer_max_memory = grpc_channel_args_find_integer(
-          args, GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE,
-          {GRPC_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE_DEFAULT, 0, INT_MAX});
-      channelz_server =
-          grpc_core::MakeRefCounted<grpc_core::channelz::ServerNode>(
-              this, channel_tracer_max_memory);
-      channelz_server->AddTraceEvent(
-          grpc_core::channelz::ChannelTrace::Severity::Info,
-          grpc_slice_from_static_string("Server created"));
-    }
-
-    if (args != nullptr) {
-      grpc_resource_quota* resource_quota =
-          grpc_resource_quota_from_channel_args(args, false /* create */);
-      if (resource_quota != nullptr) {
-        default_resource_user =
-            grpc_resource_user_create(resource_quota, "default");
-      }
-    }
-  }
-
-  ~grpc_server() {
-    grpc_channel_args_destroy(channel_args);
-    for (size_t i = 0; i < cqs.size(); i++) {
-      GRPC_CQ_INTERNAL_UNREF(cqs[i], "server");
-    }
-  }
-
-  grpc_channel_args* const channel_args;
-
-  grpc_resource_user* default_resource_user = nullptr;
-
-  std::vector<grpc_completion_queue*> cqs;
-  std::vector<grpc_pollset*> pollsets;
-  bool started = false;
-
-  /* The two following mutexes control access to server-state
-     mu_global controls access to non-call-related state (e.g., channel state)
-     mu_call controls access to call-related state (e.g., the call lists)
-
-     If they are ever required to be nested, you must lock mu_global
-     before mu_call. This is currently used in shutdown processing
-     (grpc_server_shutdown_and_notify and maybe_finish_shutdown) */
-  grpc_core::Mutex mu_global;  // mutex for server and channel state
-  grpc_core::Mutex mu_call;    // mutex for call-specific state
-
-  /* startup synchronization: flag is protected by mu_global, signals whether
-     we are doing the listener start routine or not */
-  bool starting = false;
-  grpc_core::CondVar starting_cv;
-
-  std::vector<std::unique_ptr<grpc_core::registered_method>> registered_methods;
-
-  // one request matcher for unregistered methods
-  std::unique_ptr<grpc_core::RequestMatcherInterface>
-      unregistered_request_matcher;
-
-  std::atomic_bool shutdown_flag{false};
-  bool shutdown_published = false;
-  std::vector<grpc_core::shutdown_tag> shutdown_tags;
-
-  std::list<grpc_core::channel_data*> channels;
-
-  std::list<grpc_core::Listener> listeners;
-  size_t listeners_destroyed = 0;
-  grpc_core::RefCount internal_refcount;
-
-  /** when did we print the last shutdown progress message */
-  gpr_timespec last_shutdown_message_time;
-
-  grpc_core::RefCountedPtr<grpc_core::channelz::ServerNode> channelz_server;
-};
-
-// Non-API functions of the server that are only for gRPC core internal use.
-// TODO(markdroth): Make these class member functions
-void grpc_server_add_listener(
-    grpc_server* server,
-    grpc_core::OrphanablePtr<grpc_core::ServerListenerInterface> listener) {
-  grpc_core::channelz::ListenSocketNode* listen_socket_node =
-      listener->channelz_listen_socket_node();
-  if (listen_socket_node != nullptr && server->channelz_server != nullptr) {
-    server->channelz_server->AddChildListenSocket(listen_socket_node->Ref());
-  }
-  server->listeners.emplace_back(std::move(listener));
-}
-
-const grpc_channel_args* grpc_server_get_channel_args(grpc_server* server) {
-  return server->channel_args;
-}
-
-grpc_resource_user* grpc_server_get_default_resource_user(grpc_server* server) {
-  return server->default_resource_user;
-}
-
-bool grpc_server_has_open_connections(grpc_server* server) {
-  grpc_core::MutexLock lock(&server->mu_global);
-  return !server->channels.empty();
-}
-
-grpc_core::channelz::ServerNode* grpc_server_get_channelz_node(
-    grpc_server* server) {
-  if (server == nullptr) {
-    return nullptr;
-  }
-  return server->channelz_server.get();
-}
-
-namespace grpc_core {
-namespace {
-
-void publish_call(grpc_server* server, call_data* calld, size_t cq_idx,
-                  requested_call* rc);
-void fail_call(grpc_server* server, size_t cq_idx, requested_call* rc,
-               grpc_error* error);
-/* Before calling maybe_finish_shutdown, we must hold mu_global and not
-   hold mu_call */
-void maybe_finish_shutdown(grpc_server* server);
-
-void kill_zombie(void* elem, grpc_error* /*error*/) {
-  grpc_call_unref(
-      grpc_call_from_top_element(static_cast<grpc_call_element*>(elem)));
-}
-
-// Validate a requested RPC for a server CQ and bind it to that CQ
-grpc_call_error ValidateServerRequest(
-    grpc_completion_queue* cq_for_notification, void* tag,
-    grpc_byte_buffer** optional_payload, registered_method* rm) {
-  if ((rm == nullptr && optional_payload != nullptr) ||
-      ((rm != nullptr) && ((optional_payload == nullptr) !=
-                           (rm->payload_handling == GRPC_SRM_PAYLOAD_NONE)))) {
-    return GRPC_CALL_ERROR_PAYLOAD_TYPE_MISMATCH;
-  }
-  if (grpc_cq_begin_op(cq_for_notification, tag) == false) {
-    return GRPC_CALL_ERROR_COMPLETION_QUEUE_SHUTDOWN;
-  }
-  return GRPC_CALL_OK;
-}
-
-// Validate that a requested RPC has a valid server CQ and is valid, and bind it
-grpc_call_error ValidateServerRequestAndCq(
-    size_t* cq_idx, grpc_server* server,
-    grpc_completion_queue* cq_for_notification, void* tag,
-    grpc_byte_buffer** optional_payload, registered_method* rm) {
-  size_t idx;
-  for (idx = 0; idx < server->cqs.size(); idx++) {
-    if (server->cqs[idx] == cq_for_notification) {
-      break;
-    }
-  }
-  if (idx == server->cqs.size()) {
-    return GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE;
-  }
-  grpc_call_error error =
-      ValidateServerRequest(cq_for_notification, tag, optional_payload, rm);
-  if (error != GRPC_CALL_OK) {
-    return error;
-  }
-
-  *cq_idx = idx;
-  return GRPC_CALL_OK;
-}
-/*
- * channel broadcaster
- */
-
-struct shutdown_cleanup_args {
-  grpc_closure closure;
-  grpc_slice slice;
-};
-
-void shutdown_cleanup(void* arg, grpc_error* /*error*/) {
-  shutdown_cleanup_args* a = static_cast<shutdown_cleanup_args*>(arg);
-  grpc_slice_unref_internal(a->slice);
-  delete a;
-}
-
-void send_shutdown(grpc_channel* channel, bool send_goaway,
-                   grpc_error* send_disconnect) {
-  shutdown_cleanup_args* sc = new shutdown_cleanup_args;
-  GRPC_CLOSURE_INIT(&sc->closure, shutdown_cleanup, sc,
-                    grpc_schedule_on_exec_ctx);
-  grpc_transport_op* op = grpc_make_transport_op(&sc->closure);
-  grpc_channel_element* elem;
-
-  op->goaway_error =
-      send_goaway ? grpc_error_set_int(
-                        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server shutdown"),
-                        GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_OK)
-                  : GRPC_ERROR_NONE;
-  op->set_accept_stream = true;
-  sc->slice = grpc_slice_from_copied_string("Server shutdown");
-  op->disconnect_with_error = send_disconnect;
-
-  elem = grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0);
-  elem->filter->start_transport_op(elem, op);
-}
-
-class ChannelBroadcaster {
- public:
-  // This can have an empty constructor and destructor since we want to control
-  // when the actual setup and shutdown broadcast take place
-
-  // This function copies over the channels from the locked server
-  void FillChannelsLocked(const grpc_server* s) {
-    GPR_DEBUG_ASSERT(channels_.empty());
-    channels_.reserve(s->channels.size());
-    for (const channel_data* chand : s->channels) {
-      channels_.push_back(chand->channel);
-      GRPC_CHANNEL_INTERNAL_REF(chand->channel, "broadcast");
-    }
-  }
-
-  // Broadcast a shutdown on each channel
-  void BroadcastShutdown(bool send_goaway, grpc_error* force_disconnect) {
-    for (grpc_channel* channel : channels_) {
-      send_shutdown(channel, send_goaway, GRPC_ERROR_REF(force_disconnect));
-      GRPC_CHANNEL_INTERNAL_UNREF(channel, "broadcast");
-    }
-    channels_.clear();  // just for safety against double broadcast
-    GRPC_ERROR_UNREF(force_disconnect);
-  }
-
- private:
-  std::vector<grpc_channel*> channels_;
+  virtual Server* server() const = 0;
 };
 
-/*
- * request_matcher
- */
-
 // The RealRequestMatcher is an implementation of RequestMatcherInterface that
 // actually uses all the features of RequestMatcherInterface: expecting the
 // application to explicitly request RPCs and then matching those to incoming
 // RPCs, along with a slow path by which incoming RPCs are put on a locked
 // pending list if they aren't able to be matched to an application request.
-class RealRequestMatcher : public RequestMatcherInterface {
+class Server::RealRequestMatcher : public RequestMatcherInterface {
  public:
-  explicit RealRequestMatcher(grpc_server* server)
-      : server_(server), requests_per_cq_(server->cqs.size()) {}
+  explicit RealRequestMatcher(Server* server)
+      : server_(server), requests_per_cq_(server->cqs_.size()) {}
 
   ~RealRequestMatcher() override {
     for (LockedMultiProducerSingleConsumerQueue& queue : requests_per_cq_) {
@@ -558,24 +202,20 @@ class RealRequestMatcher : public RequestMatcherInterface {
   }
 
   void ZombifyPending() override {
-    for (call_data* calld : pending_) {
-      calld->state.Store(CallState::ZOMBIED, grpc_core::MemoryOrder::RELAXED);
-      GRPC_CLOSURE_INIT(
-          &calld->kill_zombie_closure, kill_zombie,
-          grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0),
-          grpc_schedule_on_exec_ctx);
-      ExecCtx::Run(DEBUG_LOCATION, &calld->kill_zombie_closure,
-                   GRPC_ERROR_NONE);
+    while (!pending_.empty()) {
+      CallData* calld = pending_.front();
+      calld->SetState(CallData::CallState::ZOMBIED);
+      calld->KillZombie();
+      pending_.pop();
     }
-    pending_.clear();
   }
 
   void KillRequests(grpc_error* error) override {
     for (size_t i = 0; i < requests_per_cq_.size(); i++) {
-      requested_call* rc;
-      while ((rc = reinterpret_cast<requested_call*>(
+      RequestedCall* rc;
+      while ((rc = reinterpret_cast<RequestedCall*>(
                   requests_per_cq_[i].Pop())) != nullptr) {
-        fail_call(server_, i, rc, GRPC_ERROR_REF(error));
+        server_->FailCall(i, rc, GRPC_ERROR_REF(error));
       }
     }
     GRPC_ERROR_UNREF(error);
@@ -586,106 +226,90 @@ class RealRequestMatcher : public RequestMatcherInterface {
   }
 
   void RequestCallWithPossiblePublish(size_t request_queue_index,
-                                      requested_call* call) override {
+                                      RequestedCall* call) override {
     if (requests_per_cq_[request_queue_index].Push(&call->mpscq_node)) {
       /* this was the first queued request: we need to lock and start
          matching calls */
       struct PendingCall {
-        requested_call* rc = nullptr;
-        call_data* calld;
+        RequestedCall* rc = nullptr;
+        CallData* calld;
       };
       auto pop_next_pending = [this, request_queue_index] {
-        PendingCall pending;
+        PendingCall pending_call;
         {
-          MutexLock lock(&server_->mu_call);
+          MutexLock lock(&server_->mu_call_);
           if (!pending_.empty()) {
-            pending.rc = reinterpret_cast<requested_call*>(
+            pending_call.rc = reinterpret_cast<RequestedCall*>(
                 requests_per_cq_[request_queue_index].Pop());
-            if (pending.rc != nullptr) {
-              pending.calld = pending_.front();
-              pending_.pop_front();
+            if (pending_call.rc != nullptr) {
+              pending_call.calld = pending_.front();
+              pending_.pop();
             }
           }
         }
-        return pending;
+        return pending_call;
       };
       while (true) {
         PendingCall next_pending = pop_next_pending();
         if (next_pending.rc == nullptr) break;
-        CallState expect_pending = CallState::PENDING;
-        if (!next_pending.calld->state.CompareExchangeStrong(
-                &expect_pending, CallState::ACTIVATED,
-                grpc_core::MemoryOrder::ACQ_REL,
-                grpc_core::MemoryOrder::RELAXED)) {
+        if (!next_pending.calld->MaybeActivate()) {
           // Zombied Call
-          GRPC_CLOSURE_INIT(
-              &next_pending.calld->kill_zombie_closure, kill_zombie,
-              grpc_call_stack_element(
-                  grpc_call_get_call_stack(next_pending.calld->call), 0),
-              grpc_schedule_on_exec_ctx);
-          ExecCtx::Run(DEBUG_LOCATION, &next_pending.calld->kill_zombie_closure,
-                       GRPC_ERROR_NONE);
+          next_pending.calld->KillZombie();
         } else {
-          publish_call(server_, next_pending.calld, request_queue_index,
-                       next_pending.rc);
+          next_pending.calld->Publish(request_queue_index, next_pending.rc);
         }
       }
     }
   }
 
   void MatchOrQueue(size_t start_request_queue_index,
-                    call_data* calld) override {
+                    CallData* calld) override {
     for (size_t i = 0; i < requests_per_cq_.size(); i++) {
       size_t cq_idx = (start_request_queue_index + i) % requests_per_cq_.size();
-      requested_call* rc =
-          reinterpret_cast<requested_call*>(requests_per_cq_[cq_idx].TryPop());
-      if (rc == nullptr) {
-        continue;
-      } else {
+      RequestedCall* rc =
+          reinterpret_cast<RequestedCall*>(requests_per_cq_[cq_idx].TryPop());
+      if (rc != nullptr) {
         GRPC_STATS_INC_SERVER_CQS_CHECKED(i);
-        calld->state.Store(CallState::ACTIVATED,
-                           grpc_core::MemoryOrder::RELAXED);
-        publish_call(server_, calld, cq_idx, rc);
-        return; /* early out */
+        calld->SetState(CallData::CallState::ACTIVATED);
+        calld->Publish(cq_idx, rc);
+        return;
       }
     }
-
-    /* no cq to take the request found: queue it on the slow list */
+    // No cq to take the request found; queue it on the slow list.
     GRPC_STATS_INC_SERVER_SLOWPATH_REQUESTS_QUEUED();
-
     // We need to ensure that all the queues are empty.  We do this under
-    // the server mu_call lock to ensure that if something is added to
+    // the server mu_call_ lock to ensure that if something is added to
     // an empty request queue, it will block until the call is actually
     // added to the pending list.
-    requested_call* rc = nullptr;
+    RequestedCall* rc = nullptr;
     size_t cq_idx = 0;
     size_t loop_count;
     {
-      MutexLock lock(&server_->mu_call);
+      MutexLock lock(&server_->mu_call_);
       for (loop_count = 0; loop_count < requests_per_cq_.size(); loop_count++) {
         cq_idx =
             (start_request_queue_index + loop_count) % requests_per_cq_.size();
-        rc = reinterpret_cast<requested_call*>(requests_per_cq_[cq_idx].Pop());
+        rc = reinterpret_cast<RequestedCall*>(requests_per_cq_[cq_idx].Pop());
         if (rc != nullptr) {
           break;
         }
       }
       if (rc == nullptr) {
-        calld->state.Store(CallState::PENDING, grpc_core::MemoryOrder::RELAXED);
-        pending_.push_back(calld);
+        calld->SetState(CallData::CallState::PENDING);
+        pending_.push(calld);
         return;
       }
     }
     GRPC_STATS_INC_SERVER_CQS_CHECKED(loop_count + requests_per_cq_.size());
-    calld->state.Store(CallState::ACTIVATED, grpc_core::MemoryOrder::RELAXED);
-    publish_call(server_, calld, cq_idx, rc);
+    calld->SetState(CallData::CallState::ACTIVATED);
+    calld->Publish(cq_idx, rc);
   }
 
-  grpc_server* server() const override { return server_; }
+  Server* server() const override { return server_; }
 
  private:
-  grpc_server* const server_;
-  std::list<call_data*> pending_;
+  Server* const server_;
+  std::queue<CallData*> pending_;
   std::vector<LockedMultiProducerSingleConsumerQueue> requests_per_cq_;
 };
 
@@ -694,17 +318,17 @@ class RealRequestMatcher : public RequestMatcherInterface {
 // will call out to an allocation function passed in at the construction of the
 // object. These request matchers are designed for the C++ callback API, so they
 // only support 1 completion queue (passed in at the constructor).
-class AllocatingRequestMatcherBase : public RequestMatcherInterface {
+class Server::AllocatingRequestMatcherBase : public RequestMatcherInterface {
  public:
-  AllocatingRequestMatcherBase(grpc_server* server, grpc_completion_queue* cq)
+  AllocatingRequestMatcherBase(Server* server, grpc_completion_queue* cq)
       : server_(server), cq_(cq) {
     size_t idx;
-    for (idx = 0; idx < server->cqs.size(); idx++) {
-      if (server->cqs[idx] == cq) {
+    for (idx = 0; idx < server->cqs_.size(); idx++) {
+      if (server->cqs_[idx] == cq) {
         break;
       }
     }
-    GPR_ASSERT(idx < server->cqs.size());
+    GPR_ASSERT(idx < server->cqs_.size());
     cq_idx_ = idx;
   }
 
@@ -715,11 +339,11 @@ class AllocatingRequestMatcherBase : public RequestMatcherInterface {
   size_t request_queue_count() const override { return 0; }
 
   void RequestCallWithPossiblePublish(size_t /*request_queue_index*/,
-                                      requested_call* /*call*/) final {
+                                      RequestedCall* /*call*/) final {
     GPR_ASSERT(false);
   }
 
-  grpc_server* server() const override { return server_; }
+  Server* server() const override { return server_; }
 
   // Supply the completion queue related to this request matcher
   grpc_completion_queue* cq() const { return cq_; }
@@ -728,433 +352,746 @@ class AllocatingRequestMatcherBase : public RequestMatcherInterface {
   size_t cq_idx() const { return cq_idx_; }
 
  private:
-  grpc_server* const server_;
+  Server* const server_;
   grpc_completion_queue* const cq_;
   size_t cq_idx_;
 };
 
 // An allocating request matcher for non-registered methods (used for generic
 // API and unimplemented RPCs).
-class AllocatingRequestMatcherBatch : public AllocatingRequestMatcherBase {
+class Server::AllocatingRequestMatcherBatch
+    : public AllocatingRequestMatcherBase {
  public:
-  AllocatingRequestMatcherBatch(
-      grpc_server* server, grpc_completion_queue* cq,
-      std::function<ServerBatchCallAllocation()> allocator)
+  AllocatingRequestMatcherBatch(Server* server, grpc_completion_queue* cq,
+                                std::function<BatchCallAllocation()> allocator)
       : AllocatingRequestMatcherBase(server, cq),
         allocator_(std::move(allocator)) {}
+
   void MatchOrQueue(size_t /*start_request_queue_index*/,
-                    call_data* calld) override {
-    ServerBatchCallAllocation call_info = allocator_();
-    GPR_ASSERT(ValidateServerRequest(cq(), static_cast<void*>(call_info.tag),
-                                     nullptr, nullptr) == GRPC_CALL_OK);
-    requested_call* rc = new requested_call(
+                    CallData* calld) override {
+    BatchCallAllocation call_info = allocator_();
+    GPR_ASSERT(server()->ValidateServerRequest(
+                   cq(), static_cast<void*>(call_info.tag), nullptr, nullptr) ==
+               GRPC_CALL_OK);
+    RequestedCall* rc = new RequestedCall(
         static_cast<void*>(call_info.tag), cq(), call_info.call,
         call_info.initial_metadata, call_info.details);
-    calld->state.Store(CallState::ACTIVATED, grpc_core::MemoryOrder::RELAXED);
-    publish_call(server(), calld, cq_idx(), rc);
+    calld->SetState(CallData::CallState::ACTIVATED);
+    calld->Publish(cq_idx(), rc);
   }
 
  private:
-  std::function<ServerBatchCallAllocation()> allocator_;
+  std::function<BatchCallAllocation()> allocator_;
 };
 
 // An allocating request matcher for registered methods.
-class AllocatingRequestMatcherRegistered : public AllocatingRequestMatcherBase {
+class Server::AllocatingRequestMatcherRegistered
+    : public AllocatingRequestMatcherBase {
  public:
   AllocatingRequestMatcherRegistered(
-      grpc_server* server, grpc_completion_queue* cq, registered_method* rm,
-      std::function<ServerRegisteredCallAllocation()> allocator)
+      Server* server, grpc_completion_queue* cq, RegisteredMethod* rm,
+      std::function<RegisteredCallAllocation()> allocator)
       : AllocatingRequestMatcherBase(server, cq),
         registered_method_(rm),
         allocator_(std::move(allocator)) {}
+
   void MatchOrQueue(size_t /*start_request_queue_index*/,
-                    call_data* calld) override {
-    ServerRegisteredCallAllocation call_info = allocator_();
-    GPR_ASSERT(ValidateServerRequest(cq(), static_cast<void*>(call_info.tag),
-                                     call_info.optional_payload,
-                                     registered_method_) == GRPC_CALL_OK);
-    requested_call* rc = new requested_call(
+                    CallData* calld) override {
+    RegisteredCallAllocation call_info = allocator_();
+    GPR_ASSERT(
+        server()->ValidateServerRequest(cq(), static_cast<void*>(call_info.tag),
+                                        call_info.optional_payload,
+                                        registered_method_) == GRPC_CALL_OK);
+    RequestedCall* rc = new RequestedCall(
         static_cast<void*>(call_info.tag), cq(), call_info.call,
         call_info.initial_metadata, registered_method_, call_info.deadline,
         call_info.optional_payload);
-    calld->state.Store(CallState::ACTIVATED, grpc_core::MemoryOrder::RELAXED);
-    publish_call(server(), calld, cq_idx(), rc);
+    calld->SetState(CallData::CallState::ACTIVATED);
+    calld->Publish(cq_idx(), rc);
   }
 
  private:
-  registered_method* const registered_method_;
-  std::function<ServerRegisteredCallAllocation()> allocator_;
+  RegisteredMethod* const registered_method_;
+  std::function<RegisteredCallAllocation()> allocator_;
 };
 
-/*
- * server proper
- */
+//
+// ChannelBroadcaster
+//
+
+namespace {
 
-void server_ref(grpc_server* server) { server->internal_refcount.Ref(); }
+class ChannelBroadcaster {
+ public:
+  // This can have an empty constructor and destructor since we want to control
+  // when the actual setup and shutdown broadcast take place.
 
-void server_unref(grpc_server* server) {
-  if (GPR_UNLIKELY(server->internal_refcount.Unref())) {
-    delete server;
+  // Copies over the channels from the locked server.
+  void FillChannelsLocked(std::vector<grpc_channel*> channels) {
+    GPR_DEBUG_ASSERT(channels_.empty());
+    channels_ = std::move(channels);
   }
-}
 
-void finish_destroy_channel(void* cd, grpc_error* /*error*/) {
-  channel_data* chand = static_cast<channel_data*>(cd);
-  grpc_server* server = chand->server;
-  GRPC_CHANNEL_INTERNAL_UNREF(chand->channel, "server");
-  server_unref(server);
-}
+  // Broadcasts a shutdown on each channel.
+  void BroadcastShutdown(bool send_goaway, grpc_error* force_disconnect) {
+    for (grpc_channel* channel : channels_) {
+      SendShutdown(channel, send_goaway, GRPC_ERROR_REF(force_disconnect));
+      GRPC_CHANNEL_INTERNAL_UNREF(channel, "broadcast");
+    }
+    channels_.clear();  // just for safety against double broadcast
+    GRPC_ERROR_UNREF(force_disconnect);
+  }
 
-void destroy_channel(channel_data* chand) {
-  if (!chand->list_position.has_value()) return;
-  GPR_ASSERT(chand->server != nullptr);
-  chand->server->channels.erase(*chand->list_position);
-  chand->list_position.reset();
-  server_ref(chand->server);
-  maybe_finish_shutdown(chand->server);
-  GRPC_CLOSURE_INIT(&chand->finish_destroy_channel_closure,
-                    finish_destroy_channel, chand, grpc_schedule_on_exec_ctx);
+ private:
+  struct ShutdownCleanupArgs {
+    grpc_closure closure;
+    grpc_slice slice;
+  };
 
-  if (GRPC_TRACE_FLAG_ENABLED(grpc_server_channel_trace)) {
-    gpr_log(GPR_INFO, "Disconnected client");
+  static void ShutdownCleanup(void* arg, grpc_error* /*error*/) {
+    ShutdownCleanupArgs* a = static_cast<ShutdownCleanupArgs*>(arg);
+    grpc_slice_unref_internal(a->slice);
+    delete a;
   }
 
-  grpc_transport_op* op =
-      grpc_make_transport_op(&chand->finish_destroy_channel_closure);
-  op->set_accept_stream = true;
-  grpc_channel_next_op(grpc_channel_stack_element(
-                           grpc_channel_get_channel_stack(chand->channel), 0),
-                       op);
-}
+  static void SendShutdown(grpc_channel* channel, bool send_goaway,
+                           grpc_error* send_disconnect) {
+    ShutdownCleanupArgs* sc = new ShutdownCleanupArgs;
+    GRPC_CLOSURE_INIT(&sc->closure, ShutdownCleanup, sc,
+                      grpc_schedule_on_exec_ctx);
+    grpc_transport_op* op = grpc_make_transport_op(&sc->closure);
+    grpc_channel_element* elem;
+    op->goaway_error =
+        send_goaway
+            ? grpc_error_set_int(
+                  GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server shutdown"),
+                  GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_OK)
+            : GRPC_ERROR_NONE;
+    op->set_accept_stream = true;
+    sc->slice = grpc_slice_from_copied_string("Server shutdown");
+    op->disconnect_with_error = send_disconnect;
+    elem =
+        grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0);
+    elem->filter->start_transport_op(elem, op);
+  }
 
-void done_request_event(void* req, grpc_cq_completion* /*c*/) {
-  delete static_cast<requested_call*>(req);
-}
+  std::vector<grpc_channel*> channels_;
+};
 
-void publish_call(grpc_server* server, call_data* calld, size_t cq_idx,
-                  requested_call* rc) {
-  grpc_call_set_completion_queue(calld->call, rc->cq_bound_to_call);
-  grpc_call* call = calld->call;
-  *rc->call = call;
-  calld->cq_new = server->cqs[cq_idx];
-  GPR_SWAP(grpc_metadata_array, *rc->initial_metadata, calld->initial_metadata);
-  switch (rc->type) {
-    case RequestedCallType::BATCH_CALL:
-      GPR_ASSERT(calld->host_set);
-      GPR_ASSERT(calld->path_set);
-      rc->data.batch.details->host = grpc_slice_ref_internal(calld->host);
-      rc->data.batch.details->method = grpc_slice_ref_internal(calld->path);
-      rc->data.batch.details->deadline =
-          grpc_millis_to_timespec(calld->deadline, GPR_CLOCK_MONOTONIC);
-      rc->data.batch.details->flags = calld->recv_initial_metadata_flags;
-      break;
-    case RequestedCallType::REGISTERED_CALL:
-      *rc->data.registered.deadline =
-          grpc_millis_to_timespec(calld->deadline, GPR_CLOCK_MONOTONIC);
-      if (rc->data.registered.optional_payload) {
-        *rc->data.registered.optional_payload = calld->payload;
-        calld->payload = nullptr;
-      }
-      break;
-    default:
-      GPR_UNREACHABLE_CODE(return );
-  }
+}  // namespace
 
-  grpc_cq_end_op(calld->cq_new, rc->tag, GRPC_ERROR_NONE, done_request_event,
-                 rc, &rc->completion, true);
-}
+//
+// Server
+//
 
-void publish_new_rpc(void* arg, grpc_error* error) {
-  grpc_call_element* call_elem = static_cast<grpc_call_element*>(arg);
-  call_data* calld = static_cast<call_data*>(call_elem->call_data);
-  channel_data* chand = static_cast<channel_data*>(call_elem->channel_data);
-  RequestMatcherInterface* rm = calld->matcher;
-  grpc_server* server = rm->server();
+const grpc_channel_filter Server::kServerTopFilter = {
+    Server::CallData::StartTransportStreamOpBatch,
+    grpc_channel_next_op,
+    sizeof(Server::CallData),
+    Server::CallData::InitCallElement,
+    grpc_call_stack_ignore_set_pollset_or_pollset_set,
+    Server::CallData::DestroyCallElement,
+    sizeof(Server::ChannelData),
+    Server::ChannelData::InitChannelElement,
+    Server::ChannelData::DestroyChannelElement,
+    grpc_channel_next_get_info,
+    "server",
+};
 
-  if (error != GRPC_ERROR_NONE ||
-      server->shutdown_flag.load(std::memory_order_acquire)) {
-    calld->state.Store(CallState::ZOMBIED, grpc_core::MemoryOrder::RELAXED);
-    GRPC_CLOSURE_INIT(
-        &calld->kill_zombie_closure, kill_zombie,
-        grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0),
-        grpc_schedule_on_exec_ctx);
-    ExecCtx::Run(DEBUG_LOCATION, &calld->kill_zombie_closure,
-                 GRPC_ERROR_REF(error));
-    return;
+namespace {
+
+grpc_resource_user* CreateDefaultResourceUser(const grpc_channel_args* args) {
+  if (args != nullptr) {
+    grpc_resource_quota* resource_quota =
+        grpc_resource_quota_from_channel_args(args, false /* create */);
+    if (resource_quota != nullptr) {
+      return grpc_resource_user_create(resource_quota, "default");
+    }
   }
+  return nullptr;
+}
 
-  rm->MatchOrQueue(chand->cq_idx, calld);
+RefCountedPtr<channelz::ServerNode> CreateChannelzNode(
+    Server* server, const grpc_channel_args* args) {
+  RefCountedPtr<channelz::ServerNode> channelz_node;
+  if (grpc_channel_args_find_bool(args, GRPC_ARG_ENABLE_CHANNELZ,
+                                  GRPC_ENABLE_CHANNELZ_DEFAULT)) {
+    size_t channel_tracer_max_memory = grpc_channel_args_find_integer(
+        args, GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE,
+        {GRPC_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE_DEFAULT, 0, INT_MAX});
+    channelz_node =
+        MakeRefCounted<channelz::ServerNode>(channel_tracer_max_memory);
+    channelz_node->AddTraceEvent(
+        channelz::ChannelTrace::Severity::Info,
+        grpc_slice_from_static_string("Server created"));
+  }
+  return channelz_node;
 }
 
-void finish_start_new_rpc(
-    grpc_server* server, grpc_call_element* elem, RequestMatcherInterface* rm,
-    grpc_server_register_method_payload_handling payload_handling) {
-  call_data* calld = static_cast<call_data*>(elem->call_data);
+}  // namespace
+
+Server::Server(const grpc_channel_args* args)
+    : channel_args_(grpc_channel_args_copy(args)),
+      default_resource_user_(CreateDefaultResourceUser(args)),
+      channelz_node_(CreateChannelzNode(this, args)) {}
 
-  if (server->shutdown_flag.load(std::memory_order_acquire)) {
-    calld->state.Store(CallState::ZOMBIED, grpc_core::MemoryOrder::RELAXED);
-    GRPC_CLOSURE_INIT(&calld->kill_zombie_closure, kill_zombie, elem,
-                      grpc_schedule_on_exec_ctx);
-    ExecCtx::Run(DEBUG_LOCATION, &calld->kill_zombie_closure, GRPC_ERROR_NONE);
-    return;
+Server::~Server() {
+  grpc_channel_args_destroy(channel_args_);
+  for (size_t i = 0; i < cqs_.size(); i++) {
+    GRPC_CQ_INTERNAL_UNREF(cqs_[i], "server");
   }
+}
 
-  calld->matcher = rm;
+void Server::AddListener(OrphanablePtr<ListenerInterface> listener) {
+  channelz::ListenSocketNode* listen_socket_node =
+      listener->channelz_listen_socket_node();
+  if (listen_socket_node != nullptr && channelz_node_ != nullptr) {
+    channelz_node_->AddChildListenSocket(listen_socket_node->Ref());
+  }
+  listeners_.emplace_back(std::move(listener));
+}
 
-  switch (payload_handling) {
-    case GRPC_SRM_PAYLOAD_NONE:
-      publish_new_rpc(elem, GRPC_ERROR_NONE);
-      break;
-    case GRPC_SRM_PAYLOAD_READ_INITIAL_BYTE_BUFFER: {
-      grpc_op op;
-      op.op = GRPC_OP_RECV_MESSAGE;
-      op.flags = 0;
-      op.reserved = nullptr;
-      op.data.recv_message.recv_message = &calld->payload;
-      GRPC_CLOSURE_INIT(&calld->publish, publish_new_rpc, elem,
-                        grpc_schedule_on_exec_ctx);
-      grpc_call_start_batch_and_execute(calld->call, &op, 1, &calld->publish);
-      break;
+void Server::Start() {
+  started_ = true;
+  for (grpc_completion_queue* cq : cqs_) {
+    if (grpc_cq_can_listen(cq)) {
+      pollsets_.push_back(grpc_cq_pollset(cq));
+    }
+  }
+  if (unregistered_request_matcher_ == nullptr) {
+    unregistered_request_matcher_ = absl::make_unique<RealRequestMatcher>(this);
+  }
+  for (std::unique_ptr<RegisteredMethod>& rm : registered_methods_) {
+    if (rm->matcher == nullptr) {
+      rm->matcher = absl::make_unique<RealRequestMatcher>(this);
     }
   }
+  {
+    MutexLock lock(&mu_global_);
+    starting_ = true;
+  }
+  for (auto& listener : listeners_) {
+    listener.listener->Start(this, &pollsets_);
+  }
+  MutexLock lock(&mu_global_);
+  starting_ = false;
+  starting_cv_.Signal();
 }
 
-void start_new_rpc(grpc_call_element* elem) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  grpc_server* server = chand->server;
-  uint32_t i;
-  uint32_t hash;
-  channel_registered_method* rm;
-
-  if (chand->registered_methods && calld->path_set && calld->host_set) {
-    /* TODO(ctiller): unify these two searches */
-    /* check for an exact match with host */
-    hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash_internal(calld->host),
-                              grpc_slice_hash_internal(calld->path));
-    for (i = 0; i <= chand->registered_method_max_probes; i++) {
-      rm = &(*chand->registered_methods)[(hash + i) %
-                                         chand->registered_methods->size()];
-      if (rm->server_registered_method == nullptr) break;
-      if (!rm->has_host) continue;
-      if (rm->host != calld->host) continue;
-      if (rm->method != calld->path) continue;
-      if ((rm->flags & GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) &&
-          0 == (calld->recv_initial_metadata_flags &
-                GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST)) {
-        continue;
-      }
-      finish_start_new_rpc(server, elem,
-                           rm->server_registered_method->matcher.get(),
-                           rm->server_registered_method->payload_handling);
-      return;
-    }
-    /* check for a wildcard method definition (no host set) */
-    hash = GRPC_MDSTR_KV_HASH(0, grpc_slice_hash_internal(calld->path));
-    for (i = 0; i <= chand->registered_method_max_probes; i++) {
-      rm = &(*chand->registered_methods)[(hash + i) %
-                                         chand->registered_methods->size()];
-      if (rm->server_registered_method == nullptr) break;
-      if (rm->has_host) continue;
-      if (rm->method != calld->path) continue;
-      if ((rm->flags & GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) &&
-          0 == (calld->recv_initial_metadata_flags &
-                GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST)) {
-        continue;
-      }
-      finish_start_new_rpc(server, elem,
-                           rm->server_registered_method->matcher.get(),
-                           rm->server_registered_method->payload_handling);
-      return;
-    }
+void Server::SetupTransport(
+    grpc_transport* transport, grpc_pollset* accepting_pollset,
+    const grpc_channel_args* args,
+    const RefCountedPtr<grpc_core::channelz::SocketNode>& socket_node,
+    grpc_resource_user* resource_user) {
+  // Create channel.
+  grpc_channel* channel = grpc_channel_create(
+      nullptr, args, GRPC_SERVER_CHANNEL, transport, resource_user);
+  ChannelData* chand = static_cast<ChannelData*>(
+      grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0)
+          ->channel_data);
+  // Set up CQs.
+  size_t cq_idx;
+  for (cq_idx = 0; cq_idx < cqs_.size(); cq_idx++) {
+    if (grpc_cq_pollset(cqs_[cq_idx]) == accepting_pollset) break;
   }
-  finish_start_new_rpc(server, elem, server->unregistered_request_matcher.get(),
-                       GRPC_SRM_PAYLOAD_NONE);
+  if (cq_idx == cqs_.size()) {
+    // Completion queue not found.  Pick a random one to publish new calls to.
+    cq_idx = static_cast<size_t>(rand()) % cqs_.size();
+  }
+  // Set up channelz node.
+  intptr_t channelz_socket_uuid = 0;
+  if (socket_node != nullptr) {
+    channelz_socket_uuid = socket_node->uuid();
+    channelz_node_->AddChildSocket(socket_node);
+  }
+  // Initialize chand.
+  chand->InitTransport(Ref(), channel, cq_idx, transport, channelz_socket_uuid);
+}
+
+bool Server::HasOpenConnections() {
+  MutexLock lock(&mu_global_);
+  return !channels_.empty();
 }
 
-void done_shutdown_event(void* server, grpc_cq_completion* /*completion*/) {
-  server_unref(static_cast<grpc_server*>(server));
+void Server::SetRegisteredMethodAllocator(
+    grpc_completion_queue* cq, void* method_tag,
+    std::function<RegisteredCallAllocation()> allocator) {
+  RegisteredMethod* rm = static_cast<RegisteredMethod*>(method_tag);
+  rm->matcher = absl::make_unique<AllocatingRequestMatcherRegistered>(
+      this, cq, rm, std::move(allocator));
 }
 
-int num_channels(grpc_server* server) { return server->channels.size(); }
+void Server::SetBatchMethodAllocator(
+    grpc_completion_queue* cq, std::function<BatchCallAllocation()> allocator) {
+  GPR_DEBUG_ASSERT(unregistered_request_matcher_ == nullptr);
+  unregistered_request_matcher_ =
+      absl::make_unique<AllocatingRequestMatcherBatch>(this, cq,
+                                                       std::move(allocator));
+}
 
-void kill_pending_work_locked(grpc_server* server, grpc_error* error) {
-  if (server->started) {
-    server->unregistered_request_matcher->KillRequests(GRPC_ERROR_REF(error));
-    server->unregistered_request_matcher->ZombifyPending();
-    for (std::unique_ptr<registered_method>& rm : server->registered_methods) {
-      rm->matcher->KillRequests(GRPC_ERROR_REF(error));
-      rm->matcher->ZombifyPending();
+void Server::RegisterCompletionQueue(grpc_completion_queue* cq) {
+  for (grpc_completion_queue* queue : cqs_) {
+    if (queue == cq) return;
+  }
+  GRPC_CQ_INTERNAL_REF(cq, "server");
+  cqs_.push_back(cq);
+}
+
+namespace {
+
+bool streq(const std::string& a, const char* b) {
+  return (a.empty() && b == nullptr) ||
+         ((b != nullptr) && !strcmp(a.c_str(), b));
+}
+
+}  // namespace
+
+Server::RegisteredMethod* Server::RegisterMethod(
+    const char* method, const char* host,
+    grpc_server_register_method_payload_handling payload_handling,
+    uint32_t flags) {
+  if (!method) {
+    gpr_log(GPR_ERROR,
+            "grpc_server_register_method method string cannot be NULL");
+    return nullptr;
+  }
+  for (std::unique_ptr<RegisteredMethod>& m : registered_methods_) {
+    if (streq(m->method, method) && streq(m->host, host)) {
+      gpr_log(GPR_ERROR, "duplicate registration for %s@%s", method,
+              host ? host : "*");
+      return nullptr;
     }
   }
-  GRPC_ERROR_UNREF(error);
+  if ((flags & ~GRPC_INITIAL_METADATA_USED_MASK) != 0) {
+    gpr_log(GPR_ERROR, "grpc_server_register_method invalid flags 0x%08x",
+            flags);
+    return nullptr;
+  }
+  registered_methods_.emplace_back(absl::make_unique<RegisteredMethod>(
+      method, host, payload_handling, flags));
+  return registered_methods_.back().get();
+}
+
+void Server::DoneRequestEvent(void* req, grpc_cq_completion* /*c*/) {
+  delete static_cast<RequestedCall*>(req);
 }
 
-void maybe_finish_shutdown(grpc_server* server) {
-  size_t i;
-  if (!server->shutdown_flag.load(std::memory_order_acquire) ||
-      server->shutdown_published) {
+void Server::FailCall(size_t cq_idx, RequestedCall* rc, grpc_error* error) {
+  *rc->call = nullptr;
+  rc->initial_metadata->count = 0;
+  GPR_ASSERT(error != GRPC_ERROR_NONE);
+  grpc_cq_end_op(cqs_[cq_idx], rc->tag, error, DoneRequestEvent, rc,
+                 &rc->completion);
+}
+
+// Before calling MaybeFinishShutdown(), we must hold mu_global_ and not
+// hold mu_call_.
+void Server::MaybeFinishShutdown() {
+  if (!shutdown_flag_.load(std::memory_order_acquire) || shutdown_published_) {
     return;
   }
-
   {
-    MutexLock lock(&server->mu_call);
-    kill_pending_work_locked(
-        server, GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server Shutdown"));
+    MutexLock lock(&mu_call_);
+    KillPendingWorkLocked(
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server Shutdown"));
   }
-
-  if (!server->channels.empty() ||
-      server->listeners_destroyed < server->listeners.size()) {
+  if (!channels_.empty() || listeners_destroyed_ < listeners_.size()) {
     if (gpr_time_cmp(gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME),
-                                  server->last_shutdown_message_time),
+                                  last_shutdown_message_time_),
                      gpr_time_from_seconds(1, GPR_TIMESPAN)) >= 0) {
-      server->last_shutdown_message_time = gpr_now(GPR_CLOCK_REALTIME);
+      last_shutdown_message_time_ = gpr_now(GPR_CLOCK_REALTIME);
       gpr_log(GPR_DEBUG,
-              "Waiting for %d channels and %" PRIuPTR "/%" PRIuPTR
+              "Waiting for %" PRIuPTR " channels and %" PRIuPTR "/%" PRIuPTR
               " listeners to be destroyed before shutting down server",
-              num_channels(server),
-              server->listeners.size() - server->listeners_destroyed,
-              server->listeners.size());
+              channels_.size(), listeners_.size() - listeners_destroyed_,
+              listeners_.size());
     }
     return;
   }
-  server->shutdown_published = 1;
-  for (i = 0; i < server->shutdown_tags.size(); i++) {
-    server_ref(server);
-    grpc_cq_end_op(server->shutdown_tags[i].cq, server->shutdown_tags[i].tag,
-                   GRPC_ERROR_NONE, done_shutdown_event, server,
-                   &server->shutdown_tags[i].completion);
+  shutdown_published_ = true;
+  for (auto& shutdown_tag : shutdown_tags_) {
+    Ref().release();
+    grpc_cq_end_op(shutdown_tag.cq, shutdown_tag.tag, GRPC_ERROR_NONE,
+                   DoneShutdownEvent, this, &shutdown_tag.completion);
   }
 }
 
-void server_on_recv_initial_metadata(void* ptr, grpc_error* error) {
-  grpc_call_element* elem = static_cast<grpc_call_element*>(ptr);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  grpc_millis op_deadline;
+void Server::KillPendingWorkLocked(grpc_error* error) {
+  if (started_) {
+    unregistered_request_matcher_->KillRequests(GRPC_ERROR_REF(error));
+    unregistered_request_matcher_->ZombifyPending();
+    for (std::unique_ptr<RegisteredMethod>& rm : registered_methods_) {
+      rm->matcher->KillRequests(GRPC_ERROR_REF(error));
+      rm->matcher->ZombifyPending();
+    }
+  }
+  GRPC_ERROR_UNREF(error);
+}
 
-  if (error == GRPC_ERROR_NONE) {
-    GPR_DEBUG_ASSERT(calld->recv_initial_metadata->idx.named.path != nullptr);
-    GPR_DEBUG_ASSERT(calld->recv_initial_metadata->idx.named.authority !=
-                     nullptr);
-    calld->path = grpc_slice_ref_internal(
-        GRPC_MDVALUE(calld->recv_initial_metadata->idx.named.path->md));
-    calld->host = grpc_slice_ref_internal(
-        GRPC_MDVALUE(calld->recv_initial_metadata->idx.named.authority->md));
-    calld->path_set = true;
-    calld->host_set = true;
-    grpc_metadata_batch_remove(calld->recv_initial_metadata, GRPC_BATCH_PATH);
-    grpc_metadata_batch_remove(calld->recv_initial_metadata,
-                               GRPC_BATCH_AUTHORITY);
-  } else {
-    GRPC_ERROR_REF(error);
+std::vector<grpc_channel*> Server::GetChannelsLocked() const {
+  std::vector<grpc_channel*> channels;
+  channels.reserve(channels_.size());
+  for (const ChannelData* chand : channels_) {
+    channels.push_back(chand->channel());
+    GRPC_CHANNEL_INTERNAL_REF(chand->channel(), "broadcast");
   }
-  op_deadline = calld->recv_initial_metadata->deadline;
-  if (op_deadline != GRPC_MILLIS_INF_FUTURE) {
-    calld->deadline = op_deadline;
+  return channels;
+}
+
+void Server::ListenerDestroyDone(void* arg, grpc_error* /*error*/) {
+  Server* server = static_cast<Server*>(arg);
+  MutexLock lock(&server->mu_global_);
+  server->listeners_destroyed_++;
+  server->MaybeFinishShutdown();
+}
+
+namespace {
+
+void DonePublishedShutdown(void* /*done_arg*/, grpc_cq_completion* storage) {
+  delete storage;
+}
+
+}  // namespace
+
+// - Kills all pending requests-for-incoming-RPC-calls (i.e., the requests made
+//   via grpc_server_request_call() and grpc_server_request_registered_call()
+//   will now be cancelled). See KillPendingWorkLocked().
+//
+// - Shuts down the listeners (i.e., the server will no longer listen on the
+//   port for new incoming channels).
+//
+// - Iterates through all channels on the server and sends shutdown msg (see
+//   ChannelBroadcaster::BroadcastShutdown() for details) to the clients via
+//   the transport layer. The transport layer then guarantees the following:
+//    -- Sends shutdown to the client (e.g., HTTP2 transport sends GOAWAY).
+//    -- If the server has outstanding calls that are in the process, the
+//       connection is NOT closed until the server is done with all those calls.
+//    -- Once there are no more calls in progress, the channel is closed.
+void Server::ShutdownAndNotify(grpc_completion_queue* cq, void* tag) {
+  ChannelBroadcaster broadcaster;
+  {
+    // Wait for startup to be finished.  Locks mu_global.
+    MutexLock lock(&mu_global_);
+    starting_cv_.WaitUntil(&mu_global_, [this] { return !starting_; });
+    // Stay locked, and gather up some stuff to do.
+    GPR_ASSERT(grpc_cq_begin_op(cq, tag));
+    if (shutdown_published_) {
+      grpc_cq_end_op(cq, tag, GRPC_ERROR_NONE, DonePublishedShutdown, nullptr,
+                     new grpc_cq_completion);
+      return;
+    }
+    shutdown_tags_.emplace_back(tag, cq);
+    if (shutdown_flag_.load(std::memory_order_acquire)) {
+      return;
+    }
+    last_shutdown_message_time_ = gpr_now(GPR_CLOCK_REALTIME);
+    broadcaster.FillChannelsLocked(GetChannelsLocked());
+    shutdown_flag_.store(true, std::memory_order_release);
+    // Collect all unregistered then registered calls.
+    {
+      MutexLock lock(&mu_call_);
+      KillPendingWorkLocked(
+          GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server Shutdown"));
+    }
+    MaybeFinishShutdown();
   }
-  if (calld->host_set && calld->path_set) {
-    /* do nothing */
-  } else {
-    /* Pass the error reference to calld->recv_initial_metadata_error */
-    grpc_error* src_error = error;
-    error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
-        "Missing :authority or :path", &src_error, 1);
-    GRPC_ERROR_UNREF(src_error);
-    calld->recv_initial_metadata_error = GRPC_ERROR_REF(error);
+  // Shutdown listeners.
+  for (auto& listener : listeners_) {
+    channelz::ListenSocketNode* channelz_listen_socket_node =
+        listener.listener->channelz_listen_socket_node();
+    if (channelz_node_ != nullptr && channelz_listen_socket_node != nullptr) {
+      channelz_node_->RemoveChildListenSocket(
+          channelz_listen_socket_node->uuid());
+    }
+    GRPC_CLOSURE_INIT(&listener.destroy_done, ListenerDestroyDone, this,
+                      grpc_schedule_on_exec_ctx);
+    listener.listener->SetOnDestroyDone(&listener.destroy_done);
+    listener.listener.reset();
   }
-  grpc_closure* closure = calld->on_done_recv_initial_metadata;
-  calld->on_done_recv_initial_metadata = nullptr;
-  if (calld->seen_recv_trailing_metadata_ready) {
-    GRPC_CALL_COMBINER_START(calld->call_combiner,
-                             &calld->recv_trailing_metadata_ready,
-                             calld->recv_trailing_metadata_error,
-                             "continue server_recv_trailing_metadata_ready");
+  broadcaster.BroadcastShutdown(/*send_goaway=*/true, GRPC_ERROR_NONE);
+}
+
+void Server::CancelAllCalls() {
+  ChannelBroadcaster broadcaster;
+  {
+    MutexLock lock(&mu_global_);
+    broadcaster.FillChannelsLocked(GetChannelsLocked());
   }
-  Closure::Run(DEBUG_LOCATION, closure, error);
+  broadcaster.BroadcastShutdown(
+      /*send_goaway=*/false,
+      GRPC_ERROR_CREATE_FROM_STATIC_STRING("Cancelling all calls"));
 }
 
-void server_recv_trailing_metadata_ready(void* user_data, grpc_error* error) {
-  grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  if (calld->on_done_recv_initial_metadata != nullptr) {
-    calld->recv_trailing_metadata_error = GRPC_ERROR_REF(error);
-    calld->seen_recv_trailing_metadata_ready = true;
-    GRPC_CLOSURE_INIT(&calld->recv_trailing_metadata_ready,
-                      server_recv_trailing_metadata_ready, elem,
-                      grpc_schedule_on_exec_ctx);
-    GRPC_CALL_COMBINER_STOP(calld->call_combiner,
-                            "deferring server_recv_trailing_metadata_ready "
-                            "until after server_on_recv_initial_metadata");
-    return;
+void Server::Orphan() {
+  {
+    MutexLock lock(&mu_global_);
+    GPR_ASSERT(shutdown_flag_.load(std::memory_order_acquire) ||
+               listeners_.empty());
+    GPR_ASSERT(listeners_destroyed_ == listeners_.size());
   }
-  error =
-      grpc_error_add_child(GRPC_ERROR_REF(error),
-                           GRPC_ERROR_REF(calld->recv_initial_metadata_error));
-  Closure::Run(DEBUG_LOCATION, calld->original_recv_trailing_metadata_ready,
-               error);
+  if (default_resource_user_ != nullptr) {
+    grpc_resource_quota_unref(grpc_resource_user_quota(default_resource_user_));
+    grpc_resource_user_shutdown(default_resource_user_);
+    grpc_resource_user_unref(default_resource_user_);
+  }
+  Unref();
+}
+
+grpc_call_error Server::ValidateServerRequest(
+    grpc_completion_queue* cq_for_notification, void* tag,
+    grpc_byte_buffer** optional_payload, RegisteredMethod* rm) {
+  if ((rm == nullptr && optional_payload != nullptr) ||
+      ((rm != nullptr) && ((optional_payload == nullptr) !=
+                           (rm->payload_handling == GRPC_SRM_PAYLOAD_NONE)))) {
+    return GRPC_CALL_ERROR_PAYLOAD_TYPE_MISMATCH;
+  }
+  if (grpc_cq_begin_op(cq_for_notification, tag) == false) {
+    return GRPC_CALL_ERROR_COMPLETION_QUEUE_SHUTDOWN;
+  }
+  return GRPC_CALL_OK;
 }
 
-void server_mutate_op(grpc_call_element* elem,
-                      grpc_transport_stream_op_batch* op) {
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-
-  if (op->recv_initial_metadata) {
-    GPR_ASSERT(op->payload->recv_initial_metadata.recv_flags == nullptr);
-    calld->recv_initial_metadata =
-        op->payload->recv_initial_metadata.recv_initial_metadata;
-    calld->on_done_recv_initial_metadata =
-        op->payload->recv_initial_metadata.recv_initial_metadata_ready;
-    op->payload->recv_initial_metadata.recv_initial_metadata_ready =
-        &calld->on_recv_initial_metadata;
-    op->payload->recv_initial_metadata.recv_flags =
-        &calld->recv_initial_metadata_flags;
-  }
-  if (op->recv_trailing_metadata) {
-    calld->original_recv_trailing_metadata_ready =
-        op->payload->recv_trailing_metadata.recv_trailing_metadata_ready;
-    op->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
-        &calld->recv_trailing_metadata_ready;
+grpc_call_error Server::ValidateServerRequestAndCq(
+    size_t* cq_idx, grpc_completion_queue* cq_for_notification, void* tag,
+    grpc_byte_buffer** optional_payload, RegisteredMethod* rm) {
+  size_t idx;
+  for (idx = 0; idx < cqs_.size(); idx++) {
+    if (cqs_[idx] == cq_for_notification) {
+      break;
+    }
+  }
+  if (idx == cqs_.size()) {
+    return GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE;
   }
+  grpc_call_error error =
+      ValidateServerRequest(cq_for_notification, tag, optional_payload, rm);
+  if (error != GRPC_CALL_OK) {
+    return error;
+  }
+  *cq_idx = idx;
+  return GRPC_CALL_OK;
 }
 
-void server_start_transport_stream_op_batch(
-    grpc_call_element* elem, grpc_transport_stream_op_batch* op) {
-  server_mutate_op(elem, op);
-  grpc_call_next_op(elem, op);
+grpc_call_error Server::QueueRequestedCall(size_t cq_idx, RequestedCall* rc) {
+  if (shutdown_flag_.load(std::memory_order_acquire)) {
+    FailCall(cq_idx, rc,
+             GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server Shutdown"));
+    return GRPC_CALL_OK;
+  }
+  RequestMatcherInterface* rm;
+  switch (rc->type) {
+    case RequestedCall::Type::BATCH_CALL:
+      rm = unregistered_request_matcher_.get();
+      break;
+    case RequestedCall::Type::REGISTERED_CALL:
+      rm = rc->data.registered.method->matcher.get();
+      break;
+  }
+  rm->RequestCallWithPossiblePublish(cq_idx, rc);
+  return GRPC_CALL_OK;
 }
 
-void got_initial_metadata(void* ptr, grpc_error* error) {
-  grpc_call_element* elem = static_cast<grpc_call_element*>(ptr);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  if (error == GRPC_ERROR_NONE) {
-    start_new_rpc(elem);
-  } else {
-    CallState expect_not_started = CallState::NOT_STARTED;
-    CallState expect_pending = CallState::PENDING;
-    if (calld->state.CompareExchangeStrong(
-            &expect_not_started, CallState::ZOMBIED,
-            grpc_core::MemoryOrder::ACQ_REL, grpc_core::MemoryOrder::RELAXED)) {
-      GRPC_CLOSURE_INIT(&calld->kill_zombie_closure, kill_zombie, elem,
-                        grpc_schedule_on_exec_ctx);
-      ExecCtx::Run(DEBUG_LOCATION, &calld->kill_zombie_closure,
-                   GRPC_ERROR_NONE);
-    } else if (calld->state.CompareExchangeStrong(
-                   &expect_pending, CallState::ZOMBIED,
-                   grpc_core::MemoryOrder::ACQ_REL,
-                   grpc_core::MemoryOrder::RELAXED)) {
-      /* zombied call will be destroyed when it's removed from the pending
-         queue... later */
+grpc_call_error Server::RequestCall(grpc_call** call,
+                                    grpc_call_details* details,
+                                    grpc_metadata_array* request_metadata,
+                                    grpc_completion_queue* cq_bound_to_call,
+                                    grpc_completion_queue* cq_for_notification,
+                                    void* tag) {
+  size_t cq_idx;
+  grpc_call_error error = ValidateServerRequestAndCq(
+      &cq_idx, cq_for_notification, tag, nullptr, nullptr);
+  if (error != GRPC_CALL_OK) {
+    return error;
+  }
+  RequestedCall* rc =
+      new RequestedCall(tag, cq_bound_to_call, call, request_metadata, details);
+  return QueueRequestedCall(cq_idx, rc);
+}
+
+grpc_call_error Server::RequestRegisteredCall(
+    RegisteredMethod* rm, grpc_call** call, gpr_timespec* deadline,
+    grpc_metadata_array* request_metadata, grpc_byte_buffer** optional_payload,
+    grpc_completion_queue* cq_bound_to_call,
+    grpc_completion_queue* cq_for_notification, void* tag_new) {
+  size_t cq_idx;
+  grpc_call_error error = ValidateServerRequestAndCq(
+      &cq_idx, cq_for_notification, tag_new, optional_payload, rm);
+  if (error != GRPC_CALL_OK) {
+    return error;
+  }
+  RequestedCall* rc =
+      new RequestedCall(tag_new, cq_bound_to_call, call, request_metadata, rm,
+                        deadline, optional_payload);
+  return QueueRequestedCall(cq_idx, rc);
+}
+
+//
+// Server::ChannelData::ConnectivityWatcher
+//
+
+class Server::ChannelData::ConnectivityWatcher
+    : public AsyncConnectivityStateWatcherInterface {
+ public:
+  explicit ConnectivityWatcher(ChannelData* chand) : chand_(chand) {
+    GRPC_CHANNEL_INTERNAL_REF(chand_->channel_, "connectivity");
+  }
+
+  ~ConnectivityWatcher() {
+    GRPC_CHANNEL_INTERNAL_UNREF(chand_->channel_, "connectivity");
+  }
+
+ private:
+  void OnConnectivityStateChange(grpc_connectivity_state new_state,
+                                 const absl::Status& /*status*/) override {
+    // Don't do anything until we are being shut down.
+    if (new_state != GRPC_CHANNEL_SHUTDOWN) return;
+    // Shut down channel.
+    MutexLock lock(&chand_->server_->mu_global_);
+    chand_->Destroy();
+  }
+
+  ChannelData* chand_;
+};
+
+//
+// Server::ChannelData
+//
+
+Server::ChannelData::~ChannelData() {
+  if (registered_methods_ != nullptr) {
+    for (const ChannelRegisteredMethod& crm : *registered_methods_) {
+      grpc_slice_unref_internal(crm.method);
+      GPR_DEBUG_ASSERT(crm.method.refcount == &kNoopRefcount ||
+                       crm.method.refcount == nullptr);
+      if (crm.has_host) {
+        grpc_slice_unref_internal(crm.host);
+        GPR_DEBUG_ASSERT(crm.host.refcount == &kNoopRefcount ||
+                         crm.host.refcount == nullptr);
+      }
+    }
+    registered_methods_.reset();
+  }
+  if (server_ != nullptr) {
+    if (server_->channelz_node_ != nullptr && channelz_socket_uuid_ != 0) {
+      server_->channelz_node_->RemoveChildSocket(channelz_socket_uuid_);
+    }
+    {
+      MutexLock lock(&server_->mu_global_);
+      if (list_position_.has_value()) {
+        server_->channels_.erase(*list_position_);
+        list_position_.reset();
+      }
+      server_->MaybeFinishShutdown();
+    }
+  }
+}
+
+void Server::ChannelData::InitTransport(RefCountedPtr<Server> server,
+                                        grpc_channel* channel, size_t cq_idx,
+                                        grpc_transport* transport,
+                                        intptr_t channelz_socket_uuid) {
+  server_ = std::move(server);
+  channel_ = channel;
+  cq_idx_ = cq_idx;
+  channelz_socket_uuid_ = channelz_socket_uuid;
+  // Build a lookup table phrased in terms of mdstr's in this channels context
+  // to quickly find registered methods.
+  size_t num_registered_methods = server_->registered_methods_.size();
+  if (num_registered_methods > 0) {
+    uint32_t max_probes = 0;
+    size_t slots = 2 * num_registered_methods;
+    registered_methods_.reset(new std::vector<ChannelRegisteredMethod>(slots));
+    for (std::unique_ptr<RegisteredMethod>& rm : server_->registered_methods_) {
+      ExternallyManagedSlice host;
+      ExternallyManagedSlice method(rm->method.c_str());
+      const bool has_host = !rm->host.empty();
+      if (has_host) {
+        host = ExternallyManagedSlice(rm->host.c_str());
+      }
+      uint32_t hash =
+          GRPC_MDSTR_KV_HASH(has_host ? host.Hash() : 0, method.Hash());
+      uint32_t probes = 0;
+      for (probes = 0; (*registered_methods_)[(hash + probes) % slots]
+                           .server_registered_method != nullptr;
+           probes++) {
+      }
+      if (probes > max_probes) max_probes = probes;
+      ChannelRegisteredMethod* crm =
+          &(*registered_methods_)[(hash + probes) % slots];
+      crm->server_registered_method = rm.get();
+      crm->flags = rm->flags;
+      crm->has_host = has_host;
+      if (has_host) {
+        crm->host = host;
+      }
+      crm->method = method;
+    }
+    GPR_ASSERT(slots <= UINT32_MAX);
+    registered_method_max_probes_ = max_probes;
+  }
+  // Publish channel.
+  {
+    MutexLock lock(&server_->mu_global_);
+    server_->channels_.push_front(this);
+    list_position_ = server_->channels_.begin();
+  }
+  // Start accept_stream transport op.
+  grpc_transport_op* op = grpc_make_transport_op(nullptr);
+  op->set_accept_stream = true;
+  op->set_accept_stream_fn = AcceptStream;
+  op->set_accept_stream_user_data = this;
+  op->start_connectivity_watch = MakeOrphanable<ConnectivityWatcher>(this);
+  if (server_->shutdown_flag_.load(std::memory_order_acquire)) {
+    op->disconnect_with_error =
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server shutdown");
+  }
+  grpc_transport_perform_op(transport, op);
+}
+
+Server::ChannelRegisteredMethod* Server::ChannelData::GetRegisteredMethod(
+    const grpc_slice& host, const grpc_slice& path, bool is_idempotent) {
+  if (registered_methods_ == nullptr) return nullptr;
+  /* TODO(ctiller): unify these two searches */
+  /* check for an exact match with host */
+  uint32_t hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash_internal(host),
+                                     grpc_slice_hash_internal(path));
+  for (size_t i = 0; i <= registered_method_max_probes_; i++) {
+    ChannelRegisteredMethod* rm =
+        &(*registered_methods_)[(hash + i) % registered_methods_->size()];
+    if (rm->server_registered_method == nullptr) break;
+    if (!rm->has_host) continue;
+    if (rm->host != host) continue;
+    if (rm->method != path) continue;
+    if ((rm->flags & GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) &&
+        !is_idempotent) {
+      continue;
+    }
+    return rm;
+  }
+  /* check for a wildcard method definition (no host set) */
+  hash = GRPC_MDSTR_KV_HASH(0, grpc_slice_hash_internal(path));
+  for (size_t i = 0; i <= registered_method_max_probes_; i++) {
+    ChannelRegisteredMethod* rm =
+        &(*registered_methods_)[(hash + i) % registered_methods_->size()];
+    if (rm->server_registered_method == nullptr) break;
+    if (rm->has_host) continue;
+    if (rm->method != path) continue;
+    if ((rm->flags & GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) &&
+        !is_idempotent) {
+      continue;
     }
+    return rm;
   }
+  return nullptr;
 }
 
-void accept_stream(void* cd, grpc_transport* /*transport*/,
-                   const void* transport_server_data) {
-  channel_data* chand = static_cast<channel_data*>(cd);
+void Server::ChannelData::AcceptStream(void* arg, grpc_transport* /*transport*/,
+                                       const void* transport_server_data) {
+  auto* chand = static_cast<Server::ChannelData*>(arg);
   /* create a call */
   grpc_call_create_args args;
-  args.channel = chand->channel;
-  args.server = chand->server;
+  args.channel = chand->channel_;
+  args.server = chand->server_.get();
   args.parent = nullptr;
   args.propagation_mask = 0;
   args.cq = nullptr;
@@ -1167,200 +1104,352 @@ void accept_stream(void* cd, grpc_transport* /*transport*/,
   grpc_error* error = grpc_call_create(&args, &call);
   grpc_call_element* elem =
       grpc_call_stack_element(grpc_call_get_call_stack(call), 0);
+  auto* calld = static_cast<Server::CallData*>(elem->call_data);
   if (error != GRPC_ERROR_NONE) {
-    got_initial_metadata(elem, error);
     GRPC_ERROR_UNREF(error);
+    calld->FailCallCreation();
     return;
   }
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  grpc_op op;
-  op.op = GRPC_OP_RECV_INITIAL_METADATA;
-  op.flags = 0;
-  op.reserved = nullptr;
-  op.data.recv_initial_metadata.recv_initial_metadata =
-      &calld->initial_metadata;
-  GRPC_CLOSURE_INIT(&calld->got_initial_metadata, got_initial_metadata, elem,
-                    grpc_schedule_on_exec_ctx);
-  grpc_call_start_batch_and_execute(call, &op, 1, &calld->got_initial_metadata);
+  calld->Start(elem);
 }
 
-grpc_error* server_init_call_elem(grpc_call_element* elem,
-                                  const grpc_call_element_args* args) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  server_ref(chand->server);
-  new (elem->call_data) call_data(elem, *args);
-  return GRPC_ERROR_NONE;
+void Server::ChannelData::FinishDestroy(void* cd, grpc_error* /*error*/) {
+  auto* chand = static_cast<Server::ChannelData*>(cd);
+  GRPC_CHANNEL_INTERNAL_UNREF(chand->channel_, "server");
+  chand->server_->Unref();
 }
 
-void server_destroy_call_elem(grpc_call_element* elem,
-                              const grpc_call_final_info* /*final_info*/,
-                              grpc_closure* /*ignored*/) {
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  calld->~call_data();
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  server_unref(chand->server);
+void Server::ChannelData::Destroy() {
+  if (!list_position_.has_value()) return;
+  GPR_ASSERT(server_ != nullptr);
+  server_->channels_.erase(*list_position_);
+  list_position_.reset();
+  server_->Ref().release();
+  server_->MaybeFinishShutdown();
+  GRPC_CLOSURE_INIT(&finish_destroy_channel_closure_, FinishDestroy, this,
+                    grpc_schedule_on_exec_ctx);
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_server_channel_trace)) {
+    gpr_log(GPR_INFO, "Disconnected client");
+  }
+  grpc_transport_op* op =
+      grpc_make_transport_op(&finish_destroy_channel_closure_);
+  op->set_accept_stream = true;
+  grpc_channel_next_op(
+      grpc_channel_stack_element(grpc_channel_get_channel_stack(channel_), 0),
+      op);
 }
 
-grpc_error* server_init_channel_elem(grpc_channel_element* elem,
-                                     grpc_channel_element_args* args) {
+grpc_error* Server::ChannelData::InitChannelElement(
+    grpc_channel_element* elem, grpc_channel_element_args* args) {
   GPR_ASSERT(args->is_first);
   GPR_ASSERT(!args->is_last);
-
-  new (static_cast<channel_data*>(elem->channel_data)) channel_data;
-  return GRPC_ERROR_NONE;
-}
-
-channel_data::~channel_data() {
-  if (registered_methods) {
-    for (const channel_registered_method& crm : *registered_methods) {
-      grpc_slice_unref_internal(crm.method);
-      GPR_DEBUG_ASSERT(crm.method.refcount == &kNoopRefcount ||
-                       crm.method.refcount == nullptr);
-      if (crm.has_host) {
-        grpc_slice_unref_internal(crm.host);
-        GPR_DEBUG_ASSERT(crm.host.refcount == &kNoopRefcount ||
-                         crm.host.refcount == nullptr);
-      }
-    }
-  }
-  if (server) {
-    if (server->channelz_server != nullptr && channelz_socket_uuid != 0) {
-      server->channelz_server->RemoveChildSocket(channelz_socket_uuid);
-    }
-    {
-      MutexLock lock(&server->mu_global);
-      if (list_position.has_value()) {
-        server->channels.erase(*list_position);
-      }
-      maybe_finish_shutdown(server);
-    }
-    server_unref(server);
-  }
+  new (elem->channel_data) ChannelData();
+  return GRPC_ERROR_NONE;
 }
 
-void server_destroy_channel_elem(grpc_channel_element* elem) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  chand->~channel_data();
+void Server::ChannelData::DestroyChannelElement(grpc_channel_element* elem) {
+  auto* chand = static_cast<ChannelData*>(elem->channel_data);
+  chand->~ChannelData();
 }
 
-void register_completion_queue(grpc_server* server, grpc_completion_queue* cq,
-                               void* reserved) {
-  size_t i;
-  GPR_ASSERT(!reserved);
-  for (i = 0; i < server->cqs.size(); i++) {
-    if (server->cqs[i] == cq) return;
+//
+// Server::CallData
+//
+
+Server::CallData::CallData(grpc_call_element* elem,
+                           const grpc_call_element_args& args,
+                           RefCountedPtr<Server> server)
+    : server_(std::move(server)),
+      call_(grpc_call_from_top_element(elem)),
+      call_combiner_(args.call_combiner) {
+  GRPC_CLOSURE_INIT(&recv_initial_metadata_ready_, RecvInitialMetadataReady,
+                    elem, grpc_schedule_on_exec_ctx);
+  GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready_, RecvTrailingMetadataReady,
+                    elem, grpc_schedule_on_exec_ctx);
+}
+
+Server::CallData::~CallData() {
+  GPR_ASSERT(state_.Load(MemoryOrder::RELAXED) != CallState::PENDING);
+  GRPC_ERROR_UNREF(recv_initial_metadata_error_);
+  if (host_.has_value()) {
+    grpc_slice_unref_internal(*host_);
+  }
+  if (path_.has_value()) {
+    grpc_slice_unref_internal(*path_);
   }
+  grpc_metadata_array_destroy(&initial_metadata_);
+  grpc_byte_buffer_destroy(payload_);
+}
 
-  GRPC_CQ_INTERNAL_REF(cq, "server");
-  server->cqs.push_back(cq);
+void Server::CallData::SetState(CallState state) {
+  state_.Store(state, MemoryOrder::RELAXED);
 }
 
-bool streq(const std::string& a, const char* b) {
-  return (a.empty() && b == nullptr) ||
-         ((b != nullptr) && !strcmp(a.c_str(), b));
+bool Server::CallData::MaybeActivate() {
+  CallState expected = CallState::PENDING;
+  return state_.CompareExchangeStrong(&expected, CallState::ACTIVATED,
+                                      MemoryOrder::ACQ_REL,
+                                      MemoryOrder::RELAXED);
 }
 
-class ConnectivityWatcher : public AsyncConnectivityStateWatcherInterface {
- public:
-  explicit ConnectivityWatcher(channel_data* chand) : chand_(chand) {
-    GRPC_CHANNEL_INTERNAL_REF(chand_->channel, "connectivity");
+void Server::CallData::FailCallCreation() {
+  CallState expected_not_started = CallState::NOT_STARTED;
+  CallState expected_pending = CallState::PENDING;
+  if (state_.CompareExchangeStrong(&expected_not_started, CallState::ZOMBIED,
+                                   MemoryOrder::ACQ_REL,
+                                   MemoryOrder::ACQUIRE)) {
+    KillZombie();
+  } else if (state_.CompareExchangeStrong(&expected_pending, CallState::ZOMBIED,
+                                          MemoryOrder::ACQ_REL,
+                                          MemoryOrder::RELAXED)) {
+    // Zombied call will be destroyed when it's removed from the pending
+    // queue... later.
   }
+}
 
-  ~ConnectivityWatcher() {
-    GRPC_CHANNEL_INTERNAL_UNREF(chand_->channel, "connectivity");
+void Server::CallData::Start(grpc_call_element* elem) {
+  grpc_op op;
+  op.op = GRPC_OP_RECV_INITIAL_METADATA;
+  op.flags = 0;
+  op.reserved = nullptr;
+  op.data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_;
+  GRPC_CLOSURE_INIT(&recv_initial_metadata_batch_complete_,
+                    RecvInitialMetadataBatchComplete, elem,
+                    grpc_schedule_on_exec_ctx);
+  grpc_call_start_batch_and_execute(call_, &op, 1,
+                                    &recv_initial_metadata_batch_complete_);
+}
+
+void Server::CallData::Publish(size_t cq_idx, RequestedCall* rc) {
+  grpc_call_set_completion_queue(call_, rc->cq_bound_to_call);
+  *rc->call = call_;
+  cq_new_ = server_->cqs_[cq_idx];
+  GPR_SWAP(grpc_metadata_array, *rc->initial_metadata, initial_metadata_);
+  switch (rc->type) {
+    case RequestedCall::Type::BATCH_CALL:
+      GPR_ASSERT(host_.has_value());
+      GPR_ASSERT(path_.has_value());
+      rc->data.batch.details->host = grpc_slice_ref_internal(*host_);
+      rc->data.batch.details->method = grpc_slice_ref_internal(*path_);
+      rc->data.batch.details->deadline =
+          grpc_millis_to_timespec(deadline_, GPR_CLOCK_MONOTONIC);
+      rc->data.batch.details->flags = recv_initial_metadata_flags_;
+      break;
+    case RequestedCall::Type::REGISTERED_CALL:
+      *rc->data.registered.deadline =
+          grpc_millis_to_timespec(deadline_, GPR_CLOCK_MONOTONIC);
+      if (rc->data.registered.optional_payload != nullptr) {
+        *rc->data.registered.optional_payload = payload_;
+        payload_ = nullptr;
+      }
+      break;
+    default:
+      GPR_UNREACHABLE_CODE(return );
   }
+  grpc_cq_end_op(cq_new_, rc->tag, GRPC_ERROR_NONE, Server::DoneRequestEvent,
+                 rc, &rc->completion, true);
+}
 
- private:
-  void OnConnectivityStateChange(grpc_connectivity_state new_state,
-                                 const absl::Status& /* status */) override {
-    // Don't do anything until we are being shut down.
-    if (new_state != GRPC_CHANNEL_SHUTDOWN) return;
-    // Shut down channel.
-    grpc_server* server = chand_->server;
-    MutexLock lock(&server->mu_global);
-    destroy_channel(chand_);
+void Server::CallData::PublishNewRpc(void* arg, grpc_error* error) {
+  grpc_call_element* call_elem = static_cast<grpc_call_element*>(arg);
+  auto* calld = static_cast<Server::CallData*>(call_elem->call_data);
+  auto* chand = static_cast<Server::ChannelData*>(call_elem->channel_data);
+  RequestMatcherInterface* rm = calld->matcher_;
+  Server* server = rm->server();
+  if (error != GRPC_ERROR_NONE ||
+      server->shutdown_flag_.load(std::memory_order_acquire)) {
+    calld->state_.Store(CallState::ZOMBIED, MemoryOrder::RELAXED);
+    calld->KillZombie();
+    return;
   }
+  rm->MatchOrQueue(chand->cq_idx(), calld);
+}
 
-  channel_data* chand_;
-};
+namespace {
 
-void done_published_shutdown(void* /*done_arg*/, grpc_cq_completion* storage) {
-  delete storage;
+void KillZombieClosure(void* call, grpc_error* /*error*/) {
+  grpc_call_unref(static_cast<grpc_call*>(call));
 }
 
-void listener_destroy_done(void* s, grpc_error* /*error*/) {
-  grpc_server* server = static_cast<grpc_server*>(s);
-  MutexLock lock(&server->mu_global);
-  server->listeners_destroyed++;
-  maybe_finish_shutdown(server);
+}  // namespace
+
+void Server::CallData::KillZombie() {
+  GRPC_CLOSURE_INIT(&kill_zombie_closure_, KillZombieClosure, call_,
+                    grpc_schedule_on_exec_ctx);
+  ExecCtx::Run(DEBUG_LOCATION, &kill_zombie_closure_, GRPC_ERROR_NONE);
 }
 
-grpc_call_error queue_call_request(grpc_server* server, size_t cq_idx,
-                                   requested_call* rc) {
-  if (server->shutdown_flag.load(std::memory_order_acquire)) {
-    fail_call(server, cq_idx, rc,
-              GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server Shutdown"));
-    return GRPC_CALL_OK;
+void Server::CallData::StartNewRpc(grpc_call_element* elem) {
+  auto* chand = static_cast<ChannelData*>(elem->channel_data);
+  if (server_->shutdown_flag_.load(std::memory_order_acquire)) {
+    state_.Store(CallState::ZOMBIED, MemoryOrder::RELAXED);
+    KillZombie();
+    return;
   }
-  RequestMatcherInterface* rm;
-  switch (rc->type) {
-    case RequestedCallType::BATCH_CALL:
-      rm = server->unregistered_request_matcher.get();
+  // Find request matcher.
+  matcher_ = server_->unregistered_request_matcher_.get();
+  grpc_server_register_method_payload_handling payload_handling =
+      GRPC_SRM_PAYLOAD_NONE;
+  if (path_.has_value() && host_.has_value()) {
+    ChannelRegisteredMethod* rm =
+        chand->GetRegisteredMethod(*host_, *path_,
+                                   (recv_initial_metadata_flags_ &
+                                    GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST));
+    if (rm != nullptr) {
+      matcher_ = rm->server_registered_method->matcher.get();
+      payload_handling = rm->server_registered_method->payload_handling;
+    }
+  }
+  // Start recv_message op if needed.
+  switch (payload_handling) {
+    case GRPC_SRM_PAYLOAD_NONE:
+      PublishNewRpc(elem, GRPC_ERROR_NONE);
       break;
-    case RequestedCallType::REGISTERED_CALL:
-      rm = rc->data.registered.method->matcher.get();
+    case GRPC_SRM_PAYLOAD_READ_INITIAL_BYTE_BUFFER: {
+      grpc_op op;
+      op.op = GRPC_OP_RECV_MESSAGE;
+      op.flags = 0;
+      op.reserved = nullptr;
+      op.data.recv_message.recv_message = &payload_;
+      GRPC_CLOSURE_INIT(&publish_, PublishNewRpc, elem,
+                        grpc_schedule_on_exec_ctx);
+      grpc_call_start_batch_and_execute(call_, &op, 1, &publish_);
       break;
+    }
   }
-  rm->RequestCallWithPossiblePublish(cq_idx, rc);
-  return GRPC_CALL_OK;
 }
 
-void fail_call(grpc_server* server, size_t cq_idx, requested_call* rc,
-               grpc_error* error) {
-  *rc->call = nullptr;
-  rc->initial_metadata->count = 0;
-  GPR_ASSERT(error != GRPC_ERROR_NONE);
+void Server::CallData::RecvInitialMetadataBatchComplete(void* arg,
+                                                        grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
+  auto* calld = static_cast<Server::CallData*>(elem->call_data);
+  if (error != GRPC_ERROR_NONE) {
+    calld->FailCallCreation();
+    return;
+  }
+  calld->StartNewRpc(elem);
+}
 
-  grpc_cq_end_op(server->cqs[cq_idx], rc->tag, error, done_request_event, rc,
-                 &rc->completion);
+void Server::CallData::StartTransportStreamOpBatchImpl(
+    grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
+  if (batch->recv_initial_metadata) {
+    GPR_ASSERT(batch->payload->recv_initial_metadata.recv_flags == nullptr);
+    recv_initial_metadata_ =
+        batch->payload->recv_initial_metadata.recv_initial_metadata;
+    original_recv_initial_metadata_ready_ =
+        batch->payload->recv_initial_metadata.recv_initial_metadata_ready;
+    batch->payload->recv_initial_metadata.recv_initial_metadata_ready =
+        &recv_initial_metadata_ready_;
+    batch->payload->recv_initial_metadata.recv_flags =
+        &recv_initial_metadata_flags_;
+  }
+  if (batch->recv_trailing_metadata) {
+    original_recv_trailing_metadata_ready_ =
+        batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready;
+    batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
+        &recv_trailing_metadata_ready_;
+  }
+  grpc_call_next_op(elem, batch);
 }
 
-}  // namespace
+void Server::CallData::RecvInitialMetadataReady(void* ptr, grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(ptr);
+  CallData* calld = static_cast<CallData*>(elem->call_data);
+  grpc_millis op_deadline;
+  if (error == GRPC_ERROR_NONE) {
+    GPR_DEBUG_ASSERT(calld->recv_initial_metadata_->idx.named.path != nullptr);
+    GPR_DEBUG_ASSERT(calld->recv_initial_metadata_->idx.named.authority !=
+                     nullptr);
+    calld->path_.emplace(grpc_slice_ref_internal(
+        GRPC_MDVALUE(calld->recv_initial_metadata_->idx.named.path->md)));
+    calld->host_.emplace(grpc_slice_ref_internal(
+        GRPC_MDVALUE(calld->recv_initial_metadata_->idx.named.authority->md)));
+    grpc_metadata_batch_remove(calld->recv_initial_metadata_, GRPC_BATCH_PATH);
+    grpc_metadata_batch_remove(calld->recv_initial_metadata_,
+                               GRPC_BATCH_AUTHORITY);
+  } else {
+    GRPC_ERROR_REF(error);
+  }
+  op_deadline = calld->recv_initial_metadata_->deadline;
+  if (op_deadline != GRPC_MILLIS_INF_FUTURE) {
+    calld->deadline_ = op_deadline;
+  }
+  if (calld->host_.has_value() && calld->path_.has_value()) {
+    /* do nothing */
+  } else {
+    /* Pass the error reference to calld->recv_initial_metadata_error */
+    grpc_error* src_error = error;
+    error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+        "Missing :authority or :path", &src_error, 1);
+    GRPC_ERROR_UNREF(src_error);
+    calld->recv_initial_metadata_error_ = GRPC_ERROR_REF(error);
+  }
+  grpc_closure* closure = calld->original_recv_initial_metadata_ready_;
+  calld->original_recv_initial_metadata_ready_ = nullptr;
+  if (calld->seen_recv_trailing_metadata_ready_) {
+    GRPC_CALL_COMBINER_START(calld->call_combiner_,
+                             &calld->recv_trailing_metadata_ready_,
+                             calld->recv_trailing_metadata_error_,
+                             "continue server recv_trailing_metadata_ready");
+  }
+  Closure::Run(DEBUG_LOCATION, closure, error);
+}
 
-void SetServerRegisteredMethodAllocator(
-    grpc_server* server, grpc_completion_queue* cq, void* method_tag,
-    std::function<ServerRegisteredCallAllocation()> allocator) {
-  registered_method* rm = static_cast<registered_method*>(method_tag);
-  rm->matcher = absl::make_unique<AllocatingRequestMatcherRegistered>(
-      server, cq, rm, std::move(allocator));
+void Server::CallData::RecvTrailingMetadataReady(void* user_data,
+                                                 grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
+  CallData* calld = static_cast<CallData*>(elem->call_data);
+  if (calld->original_recv_initial_metadata_ready_ != nullptr) {
+    calld->recv_trailing_metadata_error_ = GRPC_ERROR_REF(error);
+    calld->seen_recv_trailing_metadata_ready_ = true;
+    GRPC_CLOSURE_INIT(&calld->recv_trailing_metadata_ready_,
+                      RecvTrailingMetadataReady, elem,
+                      grpc_schedule_on_exec_ctx);
+    GRPC_CALL_COMBINER_STOP(calld->call_combiner_,
+                            "deferring server recv_trailing_metadata_ready "
+                            "until after recv_initial_metadata_ready");
+    return;
+  }
+  error =
+      grpc_error_add_child(GRPC_ERROR_REF(error),
+                           GRPC_ERROR_REF(calld->recv_initial_metadata_error_));
+  Closure::Run(DEBUG_LOCATION, calld->original_recv_trailing_metadata_ready_,
+               error);
 }
 
-void SetServerBatchMethodAllocator(
-    grpc_server* server, grpc_completion_queue* cq,
-    std::function<ServerBatchCallAllocation()> allocator) {
-  GPR_DEBUG_ASSERT(server->unregistered_request_matcher == nullptr);
-  server->unregistered_request_matcher =
-      absl::make_unique<AllocatingRequestMatcherBatch>(server, cq,
-                                                       std::move(allocator));
+grpc_error* Server::CallData::InitCallElement(
+    grpc_call_element* elem, const grpc_call_element_args* args) {
+  auto* chand = static_cast<ChannelData*>(elem->channel_data);
+  new (elem->call_data) Server::CallData(elem, *args, chand->server());
+  return GRPC_ERROR_NONE;
+}
+
+void Server::CallData::DestroyCallElement(
+    grpc_call_element* elem, const grpc_call_final_info* /*final_info*/,
+    grpc_closure* /*ignored*/) {
+  auto* calld = static_cast<CallData*>(elem->call_data);
+  calld->~CallData();
+}
+
+void Server::CallData::StartTransportStreamOpBatch(
+    grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
+  auto* calld = static_cast<CallData*>(elem->call_data);
+  calld->StartTransportStreamOpBatchImpl(elem, batch);
 }
 
 }  // namespace grpc_core
 
-const grpc_channel_filter grpc_server_top_filter = {
-    grpc_core::server_start_transport_stream_op_batch,
-    grpc_channel_next_op,
-    sizeof(grpc_core::call_data),
-    grpc_core::server_init_call_elem,
-    grpc_call_stack_ignore_set_pollset_or_pollset_set,
-    grpc_core::server_destroy_call_elem,
-    sizeof(grpc_core::channel_data),
-    grpc_core::server_init_channel_elem,
-    grpc_core::server_destroy_channel_elem,
-    grpc_channel_next_get_info,
-    "server",
-};
+//
+// C-core API
+//
 
-// The following are core surface API functions.
+grpc_server* grpc_server_create(const grpc_channel_args* args, void* reserved) {
+  grpc_core::ExecCtx exec_ctx;
+  GRPC_API_TRACE("grpc_server_create(%p, %p)", 2, (args, reserved));
+  grpc_server* c_server = new grpc_server;
+  c_server->core_server = grpc_core::MakeOrphanable<grpc_core::Server>(args);
+  return c_server;
+}
 
 void grpc_server_register_completion_queue(grpc_server* server,
                                            grpc_completion_queue* cq,
@@ -1368,7 +1457,7 @@ void grpc_server_register_completion_queue(grpc_server* server,
   GRPC_API_TRACE(
       "grpc_server_register_completion_queue(server=%p, cq=%p, reserved=%p)", 3,
       (server, cq, reserved));
-
+  GPR_ASSERT(!reserved);
   auto cq_type = grpc_get_cq_completion_type(cq);
   if (cq_type != GRPC_CQ_NEXT && cq_type != GRPC_CQ_CALLBACK) {
     gpr_log(GPR_INFO,
@@ -1378,15 +1467,7 @@ void grpc_server_register_completion_queue(grpc_server* server,
     /* Ideally we should log an error and abort but ruby-wrapped-language API
        calls grpc_completion_queue_pluck() on server completion queues */
   }
-
-  grpc_core::register_completion_queue(server, cq, reserved);
-}
-
-grpc_server* grpc_server_create(const grpc_channel_args* args, void* reserved) {
-  grpc_core::ExecCtx exec_ctx;
-  GRPC_API_TRACE("grpc_server_create(%p, %p)", 2, (args, reserved));
-
-  return new grpc_server(args);
+  server->core_server->RegisterCompletionQueue(cq);
 }
 
 void* grpc_server_register_method(
@@ -1397,285 +1478,42 @@ void* grpc_server_register_method(
       "grpc_server_register_method(server=%p, method=%s, host=%s, "
       "flags=0x%08x)",
       4, (server, method, host, flags));
-  if (!method) {
-    gpr_log(GPR_ERROR,
-            "grpc_server_register_method method string cannot be NULL");
-    return nullptr;
-  }
-  for (std::unique_ptr<grpc_core::registered_method>& m :
-       server->registered_methods) {
-    if (grpc_core::streq(m->method, method) &&
-        grpc_core::streq(m->host, host)) {
-      gpr_log(GPR_ERROR, "duplicate registration for %s@%s", method,
-              host ? host : "*");
-      return nullptr;
-    }
-  }
-  if ((flags & ~GRPC_INITIAL_METADATA_USED_MASK) != 0) {
-    gpr_log(GPR_ERROR, "grpc_server_register_method invalid flags 0x%08x",
-            flags);
-    return nullptr;
-  }
-  server->registered_methods.emplace_back(
-      new grpc_core::registered_method(method, host, payload_handling, flags));
-  return static_cast<void*>(server->registered_methods.back().get());
+  return server->core_server->RegisterMethod(method, host, payload_handling,
+                                             flags);
 }
 
 void grpc_server_start(grpc_server* server) {
-  size_t i;
   grpc_core::ExecCtx exec_ctx;
-
   GRPC_API_TRACE("grpc_server_start(server=%p)", 1, (server));
-
-  server->started = true;
-  for (i = 0; i < server->cqs.size(); i++) {
-    if (grpc_cq_can_listen(server->cqs[i])) {
-      server->pollsets.push_back(grpc_cq_pollset(server->cqs[i]));
-    }
-  }
-  if (server->unregistered_request_matcher == nullptr) {
-    server->unregistered_request_matcher =
-        absl::make_unique<grpc_core::RealRequestMatcher>(server);
-  }
-  for (std::unique_ptr<grpc_core::registered_method>& rm :
-       server->registered_methods) {
-    if (rm->matcher == nullptr) {
-      rm->matcher = absl::make_unique<grpc_core::RealRequestMatcher>(server);
-    }
-  }
-
-  {
-    grpc_core::MutexLock lock(&server->mu_global);
-    server->starting = true;
-  }
-
-  for (auto& listener : server->listeners) {
-    listener.listener->Start(server, &server->pollsets);
-  }
-
-  grpc_core::MutexLock lock(&server->mu_global);
-  server->starting = false;
-  server->starting_cv.Signal();
+  server->core_server->Start();
 }
 
-/*
-  - Kills all pending requests-for-incoming-RPC-calls (i.e the requests made via
-    grpc_server_request_call and grpc_server_request_registered call will now be
-    cancelled). See 'kill_pending_work_locked()'
-
-  - Shuts down the listeners (i.e the server will no longer listen on the port
-    for new incoming channels).
-
-  - Iterates through all channels on the server and sends shutdown msg (see
-    'ChannelBroadcaster::BroadcastShutdown' for details) to the clients via the
-    transport layer. The transport layer then guarantees the following:
-     -- Sends shutdown to the client (for eg: HTTP2 transport sends GOAWAY)
-     -- If the server has outstanding calls that are in the process, the
-        connection is NOT closed until the server is done with all those calls
-     -- Once, there are no more calls in progress, the channel is closed
- */
 void grpc_server_shutdown_and_notify(grpc_server* server,
                                      grpc_completion_queue* cq, void* tag) {
-  grpc_core::ChannelBroadcaster broadcaster;
   grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
   grpc_core::ExecCtx exec_ctx;
-
   GRPC_API_TRACE("grpc_server_shutdown_and_notify(server=%p, cq=%p, tag=%p)", 3,
                  (server, cq, tag));
-
-  {
-    /* wait for startup to be finished: locks mu_global */
-    grpc_core::MutexLock lock(&server->mu_global);
-    server->starting_cv.WaitUntil(&server->mu_global,
-                                  [server] { return !server->starting; });
-
-    /* stay locked, and gather up some stuff to do */
-    GPR_ASSERT(grpc_cq_begin_op(cq, tag));
-    if (server->shutdown_published) {
-      grpc_cq_end_op(cq, tag, GRPC_ERROR_NONE,
-                     grpc_core::done_published_shutdown, nullptr,
-                     new grpc_cq_completion);
-      return;
-    }
-    server->shutdown_tags.emplace_back(tag, cq);
-    if (server->shutdown_flag.load(std::memory_order_acquire)) {
-      return;
-    }
-
-    server->last_shutdown_message_time = gpr_now(GPR_CLOCK_REALTIME);
-
-    broadcaster.FillChannelsLocked(server);
-
-    server->shutdown_flag.store(true, std::memory_order_release);
-
-    /* collect all unregistered then registered calls */
-    {
-      grpc_core::MutexLock lock(&server->mu_call);
-      grpc_core::kill_pending_work_locked(
-          server, GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server Shutdown"));
-    }
-
-    grpc_core::maybe_finish_shutdown(server);
-  }
-
-  /* Shutdown listeners */
-  for (auto& listener : server->listeners) {
-    grpc_core::channelz::ListenSocketNode* channelz_listen_socket_node =
-        listener.listener->channelz_listen_socket_node();
-    if (server->channelz_server != nullptr &&
-        channelz_listen_socket_node != nullptr) {
-      server->channelz_server->RemoveChildListenSocket(
-          channelz_listen_socket_node->uuid());
-    }
-    GRPC_CLOSURE_INIT(&listener.destroy_done, grpc_core::listener_destroy_done,
-                      server, grpc_schedule_on_exec_ctx);
-    listener.listener->SetOnDestroyDone(&listener.destroy_done);
-    listener.listener.reset();
-  }
-
-  broadcaster.BroadcastShutdown(/*send_goaway=*/true, GRPC_ERROR_NONE);
+  server->core_server->ShutdownAndNotify(cq, tag);
 }
 
 void grpc_server_cancel_all_calls(grpc_server* server) {
-  grpc_core::ChannelBroadcaster broadcaster;
   grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
   grpc_core::ExecCtx exec_ctx;
-
   GRPC_API_TRACE("grpc_server_cancel_all_calls(server=%p)", 1, (server));
-
-  {
-    grpc_core::MutexLock lock(&server->mu_global);
-    broadcaster.FillChannelsLocked(server);
-  }
-
-  broadcaster.BroadcastShutdown(
-      /*send_goaway=*/false,
-      GRPC_ERROR_CREATE_FROM_STATIC_STRING("Cancelling all calls"));
+  server->core_server->CancelAllCalls();
 }
 
 void grpc_server_destroy(grpc_server* server) {
   grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
   grpc_core::ExecCtx exec_ctx;
-
   GRPC_API_TRACE("grpc_server_destroy(server=%p)", 1, (server));
-
-  {
-    grpc_core::MutexLock lock(&server->mu_global);
-    GPR_ASSERT(server->shutdown_flag.load(std::memory_order_acquire) ||
-               server->listeners.empty());
-    GPR_ASSERT(server->listeners_destroyed == server->listeners.size());
-  }
-
-  if (server->default_resource_user != nullptr) {
-    grpc_resource_quota_unref(
-        grpc_resource_user_quota(server->default_resource_user));
-    grpc_resource_user_shutdown(server->default_resource_user);
-    grpc_resource_user_unref(server->default_resource_user);
-  }
-  grpc_core::server_unref(server);
-}
-
-const std::vector<grpc_pollset*>& grpc_server_get_pollsets(
-    grpc_server* server) {
-  return server->pollsets;
-}
-
-void grpc_server_setup_transport(
-    grpc_server* s, grpc_transport* transport, grpc_pollset* accepting_pollset,
-    const grpc_channel_args* args,
-    const grpc_core::RefCountedPtr<grpc_core::channelz::SocketNode>&
-        socket_node,
-    grpc_resource_user* resource_user) {
-  size_t num_registered_methods;
-  grpc_core::channel_registered_method* crm;
-  grpc_channel* channel;
-  grpc_core::channel_data* chand;
-  uint32_t hash;
-  size_t slots;
-  uint32_t probes;
-  uint32_t max_probes = 0;
-  grpc_transport_op* op = nullptr;
-
-  channel = grpc_channel_create(nullptr, args, GRPC_SERVER_CHANNEL, transport,
-                                resource_user);
-  chand = static_cast<grpc_core::channel_data*>(
-      grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0)
-          ->channel_data);
-  chand->server = s;
-  grpc_core::server_ref(s);
-  chand->channel = channel;
-  if (socket_node != nullptr) {
-    chand->channelz_socket_uuid = socket_node->uuid();
-    s->channelz_server->AddChildSocket(socket_node);
-  } else {
-    chand->channelz_socket_uuid = 0;
-  }
-
-  size_t cq_idx;
-  for (cq_idx = 0; cq_idx < s->cqs.size(); cq_idx++) {
-    if (grpc_cq_pollset(s->cqs[cq_idx]) == accepting_pollset) break;
-  }
-  if (cq_idx == s->cqs.size()) {
-    /* completion queue not found: pick a random one to publish new calls to */
-    cq_idx = static_cast<size_t>(rand()) % s->cqs.size();
-  }
-  chand->cq_idx = cq_idx;
-
-  num_registered_methods = s->registered_methods.size();
-  /* build a lookup table phrased in terms of mdstr's in this channels context
-     to quickly find registered methods */
-  if (num_registered_methods > 0) {
-    slots = 2 * num_registered_methods;
-    chand->registered_methods.reset(
-        new std::vector<grpc_core::channel_registered_method>(slots));
-    for (std::unique_ptr<grpc_core::registered_method>& rm :
-         s->registered_methods) {
-      grpc_core::ExternallyManagedSlice host;
-      grpc_core::ExternallyManagedSlice method(rm->method.c_str());
-      const bool has_host = !rm->host.empty();
-      if (has_host) {
-        host = grpc_core::ExternallyManagedSlice(rm->host.c_str());
-      }
-      hash = GRPC_MDSTR_KV_HASH(has_host ? host.Hash() : 0, method.Hash());
-      for (probes = 0; (*chand->registered_methods)[(hash + probes) % slots]
-                           .server_registered_method != nullptr;
-           probes++) {
-      }
-      if (probes > max_probes) max_probes = probes;
-      crm = &(*chand->registered_methods)[(hash + probes) % slots];
-      crm->server_registered_method = rm.get();
-      crm->flags = rm->flags;
-      crm->has_host = has_host;
-      if (has_host) {
-        crm->host = host;
-      }
-      crm->method = method;
-    }
-    GPR_ASSERT(slots <= UINT32_MAX);
-    chand->registered_method_max_probes = max_probes;
-  }
-
-  {
-    grpc_core::MutexLock lock(&s->mu_global);
-    s->channels.push_front(chand);
-    chand->list_position = s->channels.begin();
-  }
-
-  op = grpc_make_transport_op(nullptr);
-  op->set_accept_stream = true;
-  op->set_accept_stream_fn = grpc_core::accept_stream;
-  op->set_accept_stream_user_data = chand;
-  op->start_connectivity_watch.reset(new grpc_core::ConnectivityWatcher(chand));
-  if (s->shutdown_flag.load(std::memory_order_acquire)) {
-    op->disconnect_with_error =
-        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server shutdown");
-  }
-  grpc_transport_perform_op(transport, op);
+  delete server;
 }
 
 grpc_call_error grpc_server_request_call(
     grpc_server* server, grpc_call** call, grpc_call_details* details,
-    grpc_metadata_array* initial_metadata,
+    grpc_metadata_array* request_metadata,
     grpc_completion_queue* cq_bound_to_call,
     grpc_completion_queue* cq_for_notification, void* tag) {
   grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
@@ -1686,49 +1524,31 @@ grpc_call_error grpc_server_request_call(
       "server=%p, call=%p, details=%p, initial_metadata=%p, "
       "cq_bound_to_call=%p, cq_for_notification=%p, tag=%p)",
       7,
-      (server, call, details, initial_metadata, cq_bound_to_call,
+      (server, call, details, request_metadata, cq_bound_to_call,
        cq_for_notification, tag));
-
-  size_t cq_idx;
-  grpc_call_error error = grpc_core::ValidateServerRequestAndCq(
-      &cq_idx, server, cq_for_notification, tag, nullptr, nullptr);
-  if (error != GRPC_CALL_OK) {
-    return error;
-  }
-
-  grpc_core::requested_call* rc = new grpc_core::requested_call(
-      tag, cq_bound_to_call, call, initial_metadata, details);
-  return queue_call_request(server, cq_idx, rc);
+  return server->core_server->RequestCall(call, details, request_metadata,
+                                          cq_bound_to_call, cq_for_notification,
+                                          tag);
 }
 
 grpc_call_error grpc_server_request_registered_call(
     grpc_server* server, void* rmp, grpc_call** call, gpr_timespec* deadline,
-    grpc_metadata_array* initial_metadata, grpc_byte_buffer** optional_payload,
+    grpc_metadata_array* request_metadata, grpc_byte_buffer** optional_payload,
     grpc_completion_queue* cq_bound_to_call,
     grpc_completion_queue* cq_for_notification, void* tag_new) {
   grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
   grpc_core::ExecCtx exec_ctx;
   GRPC_STATS_INC_SERVER_REQUESTED_CALLS();
-  grpc_core::registered_method* rm =
-      static_cast<grpc_core::registered_method*>(rmp);
+  auto* rm = static_cast<grpc_core::Server::RegisteredMethod*>(rmp);
   GRPC_API_TRACE(
       "grpc_server_request_registered_call("
-      "server=%p, rmp=%p, call=%p, deadline=%p, initial_metadata=%p, "
+      "server=%p, rmp=%p, call=%p, deadline=%p, request_metadata=%p, "
       "optional_payload=%p, cq_bound_to_call=%p, cq_for_notification=%p, "
       "tag=%p)",
       9,
-      (server, rmp, call, deadline, initial_metadata, optional_payload,
+      (server, rmp, call, deadline, request_metadata, optional_payload,
        cq_bound_to_call, cq_for_notification, tag_new));
-
-  size_t cq_idx;
-  grpc_call_error error = ValidateServerRequestAndCq(
-      &cq_idx, server, cq_for_notification, tag_new, optional_payload, rm);
-  if (error != GRPC_CALL_OK) {
-    return error;
-  }
-
-  grpc_core::requested_call* rc = new grpc_core::requested_call(
-      tag_new, cq_bound_to_call, call, initial_metadata, rm, deadline,
-      optional_payload);
-  return queue_call_request(server, cq_idx, rc);
+  return server->core_server->RequestRegisteredCall(
+      rm, call, deadline, request_metadata, optional_payload, cq_bound_to_call,
+      cq_for_notification, tag_new);
 }
diff --git a/src/core/lib/surface/server.h b/src/core/lib/surface/server.h
index d63a640f84d..3a13b6f404e 100644
--- a/src/core/lib/surface/server.h
+++ b/src/core/lib/surface/server.h
@@ -1,121 +1,397 @@
-/*
- *
- * Copyright 2015 gRPC authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
+//
+// Copyright 2015 gRPC authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
 
 #ifndef GRPC_CORE_LIB_SURFACE_SERVER_H
 #define GRPC_CORE_LIB_SURFACE_SERVER_H
 
 #include <grpc/support/port_platform.h>
 
+#include <list>
+#include <vector>
+
+#include "absl/types/optional.h"
+
 #include <grpc/grpc.h>
+
+#include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/channel/channel_stack.h"
 #include "src/core/lib/channel/channelz.h"
 #include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gprpp/atomic.h"
+#include "src/core/lib/surface/completion_queue.h"
 #include "src/core/lib/transport/transport.h"
 
-extern const grpc_channel_filter grpc_server_top_filter;
-
-/** Lightweight tracing of server channel state */
-extern grpc_core::TraceFlag grpc_server_channel_trace;
-
 namespace grpc_core {
 
-/// Interface for listeners.
-/// Implementations must override the Orphan() method, which should stop
-/// listening and initiate destruction of the listener.
-class ServerListenerInterface : public Orphanable {
+extern TraceFlag grpc_server_channel_trace;
+
+class Server : public InternallyRefCounted<Server> {
  public:
-  virtual ~ServerListenerInterface() = default;
+  // Filter vtable.
+  static const grpc_channel_filter kServerTopFilter;
 
-  /// Starts listening. This listener may refer to the pollset object beyond
-  /// this call, so it is a pointer rather than a reference.
-  virtual void Start(grpc_server* server,
-                     const std::vector<grpc_pollset*>* pollsets) = 0;
+  // Opaque type used for registered methods.
+  struct RegisteredMethod;
 
-  /// Returns the channelz node for the listen socket, or null if not
-  /// supported.
-  virtual channelz::ListenSocketNode* channelz_listen_socket_node() const = 0;
+  // An object to represent the most relevant characteristics of a
+  // newly-allocated call object when using an AllocatingRequestMatcherBatch.
+  struct BatchCallAllocation {
+    grpc_experimental_completion_queue_functor* tag;
+    grpc_call** call;
+    grpc_metadata_array* initial_metadata;
+    grpc_call_details* details;
+  };
 
-  /// Sets a closure to be invoked by the listener when its destruction
-  /// is complete.
-  virtual void SetOnDestroyDone(grpc_closure* on_destroy_done) = 0;
-};
+  // An object to represent the most relevant characteristics of a
+  // newly-allocated call object when using an
+  // AllocatingRequestMatcherRegistered.
+  struct RegisteredCallAllocation {
+    grpc_experimental_completion_queue_functor* tag;
+    grpc_call** call;
+    grpc_metadata_array* initial_metadata;
+    gpr_timespec* deadline;
+    grpc_byte_buffer** optional_payload;
+  };
 
-}  // namespace grpc_core
+  /// Interface for listeners.
+  /// Implementations must override the Orphan() method, which should stop
+  /// listening and initiate destruction of the listener.
+  class ListenerInterface : public Orphanable {
+   public:
+    virtual ~ListenerInterface() = default;
 
-/* Add a listener to the server: when the server starts, it will call Start(),
-   and when it shuts down, it will orphan the listener. */
-void grpc_server_add_listener(
-    grpc_server* server,
-    grpc_core::OrphanablePtr<grpc_core::ServerListenerInterface> listener);
+    /// Starts listening. This listener may refer to the pollset object beyond
+    /// this call, so it is a pointer rather than a reference.
+    virtual void Start(Server* server,
+                       const std::vector<grpc_pollset*>* pollsets) = 0;
 
-/* Setup a transport - creates a channel stack, binds the transport to the
-   server */
-void grpc_server_setup_transport(
-    grpc_server* server, grpc_transport* transport,
-    grpc_pollset* accepting_pollset, const grpc_channel_args* args,
-    const grpc_core::RefCountedPtr<grpc_core::channelz::SocketNode>&
-        socket_node,
-    grpc_resource_user* resource_user = nullptr);
+    /// Returns the channelz node for the listen socket, or null if not
+    /// supported.
+    virtual channelz::ListenSocketNode* channelz_listen_socket_node() const = 0;
 
-grpc_core::channelz::ServerNode* grpc_server_get_channelz_node(
-    grpc_server* server);
+    /// Sets a closure to be invoked by the listener when its destruction
+    /// is complete.
+    virtual void SetOnDestroyDone(grpc_closure* on_destroy_done) = 0;
+  };
 
-const grpc_channel_args* grpc_server_get_channel_args(grpc_server* server);
+  explicit Server(const grpc_channel_args* args);
+  ~Server();
 
-grpc_resource_user* grpc_server_get_default_resource_user(grpc_server* server);
+  void Orphan() override;
 
-bool grpc_server_has_open_connections(grpc_server* server);
+  const grpc_channel_args* channel_args() const { return channel_args_; }
+  grpc_resource_user* default_resource_user() const {
+    return default_resource_user_;
+  }
+  channelz::ServerNode* channelz_node() const { return channelz_node_.get(); }
 
-// Do not call this before grpc_server_start. Returns the pollsets. The vector
-// itself is immutable, but the pollsets inside are mutable. The result is valid
-// for the lifetime of the server.
-const std::vector<grpc_pollset*>& grpc_server_get_pollsets(grpc_server* server);
+  // Do not call this before Start(). Returns the pollsets. The
+  // vector itself is immutable, but the pollsets inside are mutable. The
+  // result is valid for the lifetime of the server.
+  const std::vector<grpc_pollset*>& pollsets() const { return pollsets_; }
 
-namespace grpc_core {
+  bool HasOpenConnections();
 
-// An object to represent the most relevant characteristics of a newly-allocated
-// call object when using an AllocatingRequestMatcherBatch
-struct ServerBatchCallAllocation {
-  grpc_experimental_completion_queue_functor* tag;
-  grpc_call** call;
-  grpc_metadata_array* initial_metadata;
-  grpc_call_details* details;
-};
+  // Adds a listener to the server.  When the server starts, it will call
+  // the listener's Start() method, and when it shuts down, it will orphan
+  // the listener.
+  void AddListener(OrphanablePtr<ListenerInterface> listener);
 
-// An object to represent the most relevant characteristics of a newly-allocated
-// call object when using an AllocatingRequestMatcherRegistered
-struct ServerRegisteredCallAllocation {
-  grpc_experimental_completion_queue_functor* tag;
-  grpc_call** call;
-  grpc_metadata_array* initial_metadata;
-  gpr_timespec* deadline;
-  grpc_byte_buffer** optional_payload;
-};
+  // Starts listening for connections.
+  void Start();
+
+  // Sets up a transport.  Creates a channel stack and binds the transport to
+  // the server.  Called from the listener when a new connection is accepted.
+  void SetupTransport(grpc_transport* transport,
+                      grpc_pollset* accepting_pollset,
+                      const grpc_channel_args* args,
+                      const RefCountedPtr<channelz::SocketNode>& socket_node,
+                      grpc_resource_user* resource_user = nullptr);
+
+  void RegisterCompletionQueue(grpc_completion_queue* cq);
+
+  // Functions to specify that a specific registered method or the unregistered
+  // collection should use a specific allocator for request matching.
+  void SetRegisteredMethodAllocator(
+      grpc_completion_queue* cq, void* method_tag,
+      std::function<RegisteredCallAllocation()> allocator);
+  void SetBatchMethodAllocator(grpc_completion_queue* cq,
+                               std::function<BatchCallAllocation()> allocator);
+
+  RegisteredMethod* RegisterMethod(
+      const char* method, const char* host,
+      grpc_server_register_method_payload_handling payload_handling,
+      uint32_t flags);
+
+  grpc_call_error RequestCall(grpc_call** call, grpc_call_details* details,
+                              grpc_metadata_array* request_metadata,
+                              grpc_completion_queue* cq_bound_to_call,
+                              grpc_completion_queue* cq_for_notification,
+                              void* tag);
+
+  grpc_call_error RequestRegisteredCall(
+      RegisteredMethod* rm, grpc_call** call, gpr_timespec* deadline,
+      grpc_metadata_array* request_metadata,
+      grpc_byte_buffer** optional_payload,
+      grpc_completion_queue* cq_bound_to_call,
+      grpc_completion_queue* cq_for_notification, void* tag_new);
+
+  void ShutdownAndNotify(grpc_completion_queue* cq, void* tag);
+
+  void CancelAllCalls();
+
+ private:
+  struct RequestedCall;
+
+  struct ChannelRegisteredMethod {
+    RegisteredMethod* server_registered_method = nullptr;
+    uint32_t flags;
+    bool has_host;
+    ExternallyManagedSlice method;
+    ExternallyManagedSlice host;
+  };
+
+  class RequestMatcherInterface;
+  class RealRequestMatcher;
+  class AllocatingRequestMatcherBase;
+  class AllocatingRequestMatcherBatch;
+  class AllocatingRequestMatcherRegistered;
+
+  class ChannelData {
+   public:
+    ChannelData() = default;
+    ~ChannelData();
+
+    void InitTransport(RefCountedPtr<Server> server, grpc_channel* channel,
+                       size_t cq_idx, grpc_transport* transport,
+                       intptr_t channelz_socket_uuid);
+
+    RefCountedPtr<Server> server() const { return server_; }
+    grpc_channel* channel() const { return channel_; }
+    size_t cq_idx() const { return cq_idx_; }
+
+    ChannelRegisteredMethod* GetRegisteredMethod(const grpc_slice& host,
+                                                 const grpc_slice& path,
+                                                 bool is_idempotent);
+
+    // Filter vtable functions.
+    static grpc_error* InitChannelElement(grpc_channel_element* elem,
+                                          grpc_channel_element_args* args);
+    static void DestroyChannelElement(grpc_channel_element* elem);
+
+   private:
+    class ConnectivityWatcher;
+
+    static void AcceptStream(void* arg, grpc_transport* /*transport*/,
+                             const void* transport_server_data);
+
+    void Destroy();
+
+    static void FinishDestroy(void* arg, grpc_error* error);
+
+    RefCountedPtr<Server> server_;
+    grpc_channel* channel_;
+    // The index into Server::cqs_ of the CQ used as a starting point for
+    // where to publish new incoming calls.
+    size_t cq_idx_;
+    absl::optional<std::list<ChannelData*>::iterator> list_position_;
+    // A hash-table of the methods and hosts of the registered methods.
+    // TODO(vjpai): Convert this to an STL map type as opposed to a direct
+    // bucket implementation. (Consider performance impact, hash function to
+    // use, etc.)
+    std::unique_ptr<std::vector<ChannelRegisteredMethod>> registered_methods_;
+    uint32_t registered_method_max_probes_;
+    grpc_closure finish_destroy_channel_closure_;
+    intptr_t channelz_socket_uuid_;
+  };
+
+  class CallData {
+   public:
+    enum class CallState {
+      NOT_STARTED,  // Waiting for metadata.
+      PENDING,      // Initial metadata read, not flow controlled in yet.
+      ACTIVATED,    // Flow controlled in, on completion queue.
+      ZOMBIED,      // Cancelled before being queued.
+    };
+
+    CallData(grpc_call_element* elem, const grpc_call_element_args& args,
+             RefCountedPtr<Server> server);
+    ~CallData();
+
+    // Starts the recv_initial_metadata batch on the call.
+    // Invoked from ChannelData::AcceptStream().
+    void Start(grpc_call_element* elem);
+
+    void SetState(CallState state);
+
+    // Attempts to move from PENDING to ACTIVATED state.  Returns true
+    // on success.
+    bool MaybeActivate();
+
+    // Publishes an incoming call to the application after it has been
+    // matched.
+    void Publish(size_t cq_idx, RequestedCall* rc);
+
+    void KillZombie();
 
-// Functions to specify that a specific registered method or the unregistered
-// collection should use a specific allocator for request matching.
-void SetServerRegisteredMethodAllocator(
-    grpc_server* server, grpc_completion_queue* cq, void* method_tag,
-    std::function<ServerRegisteredCallAllocation()> allocator);
-void SetServerBatchMethodAllocator(
-    grpc_server* server, grpc_completion_queue* cq,
-    std::function<ServerBatchCallAllocation()> allocator);
+    void FailCallCreation();
+
+    // Filter vtable functions.
+    static grpc_error* InitCallElement(grpc_call_element* elem,
+                                       const grpc_call_element_args* args);
+    static void DestroyCallElement(grpc_call_element* elem,
+                                   const grpc_call_final_info* /*final_info*/,
+                                   grpc_closure* /*ignored*/);
+    static void StartTransportStreamOpBatch(
+        grpc_call_element* elem, grpc_transport_stream_op_batch* batch);
+
+   private:
+    // Helper functions for handling calls at the top of the call stack.
+    static void RecvInitialMetadataBatchComplete(void* arg, grpc_error* error);
+    void StartNewRpc(grpc_call_element* elem);
+    static void PublishNewRpc(void* arg, grpc_error* error);
+
+    // Functions used inside the call stack.
+    void StartTransportStreamOpBatchImpl(grpc_call_element* elem,
+                                         grpc_transport_stream_op_batch* batch);
+    static void RecvInitialMetadataReady(void* arg, grpc_error* error);
+    static void RecvTrailingMetadataReady(void* arg, grpc_error* error);
+
+    RefCountedPtr<Server> server_;
+
+    grpc_call* call_;
+
+    Atomic<CallState> state_{CallState::NOT_STARTED};
+
+    absl::optional<grpc_slice> path_;
+    absl::optional<grpc_slice> host_;
+    grpc_millis deadline_ = GRPC_MILLIS_INF_FUTURE;
+
+    grpc_completion_queue* cq_new_ = nullptr;
+
+    RequestMatcherInterface* matcher_ = nullptr;
+    grpc_byte_buffer* payload_ = nullptr;
+
+    grpc_closure kill_zombie_closure_;
+
+    grpc_metadata_array initial_metadata_ =
+        grpc_metadata_array();  // Zero-initialize the C struct.
+    grpc_closure recv_initial_metadata_batch_complete_;
+
+    grpc_metadata_batch* recv_initial_metadata_ = nullptr;
+    uint32_t recv_initial_metadata_flags_ = 0;
+    grpc_closure recv_initial_metadata_ready_;
+    grpc_closure* original_recv_initial_metadata_ready_;
+    grpc_error* recv_initial_metadata_error_ = GRPC_ERROR_NONE;
+
+    bool seen_recv_trailing_metadata_ready_ = false;
+    grpc_closure recv_trailing_metadata_ready_;
+    grpc_closure* original_recv_trailing_metadata_ready_;
+    grpc_error* recv_trailing_metadata_error_ = GRPC_ERROR_NONE;
+
+    grpc_closure publish_;
+
+    CallCombiner* call_combiner_;
+  };
+
+  struct Listener {
+    explicit Listener(OrphanablePtr<ListenerInterface> l)
+        : listener(std::move(l)) {}
+    OrphanablePtr<ListenerInterface> listener;
+    grpc_closure destroy_done;
+  };
+
+  struct ShutdownTag {
+    ShutdownTag(void* tag_arg, grpc_completion_queue* cq_arg)
+        : tag(tag_arg), cq(cq_arg) {}
+    void* const tag;
+    grpc_completion_queue* const cq;
+    grpc_cq_completion completion;
+  };
+
+  static void ListenerDestroyDone(void* arg, grpc_error* error);
+
+  static void DoneShutdownEvent(void* server,
+                                grpc_cq_completion* /*completion*/) {
+    static_cast<Server*>(server)->Unref();
+  }
+
+  static void DoneRequestEvent(void* req, grpc_cq_completion* completion);
+
+  void FailCall(size_t cq_idx, RequestedCall* rc, grpc_error* error);
+  grpc_call_error QueueRequestedCall(size_t cq_idx, RequestedCall* rc);
+
+  void MaybeFinishShutdown();
+
+  void KillPendingWorkLocked(grpc_error* error);
+
+  static grpc_call_error ValidateServerRequest(
+      grpc_completion_queue* cq_for_notification, void* tag,
+      grpc_byte_buffer** optional_payload, RegisteredMethod* rm);
+  grpc_call_error ValidateServerRequestAndCq(
+      size_t* cq_idx, grpc_completion_queue* cq_for_notification, void* tag,
+      grpc_byte_buffer** optional_payload, RegisteredMethod* rm);
+
+  std::vector<grpc_channel*> GetChannelsLocked() const;
+
+  grpc_channel_args* const channel_args_;
+  grpc_resource_user* default_resource_user_ = nullptr;
+  RefCountedPtr<channelz::ServerNode> channelz_node_;
+
+  std::vector<grpc_completion_queue*> cqs_;
+  std::vector<grpc_pollset*> pollsets_;
+  bool started_ = false;
+
+  // The two following mutexes control access to server-state.
+  // mu_global_ controls access to non-call-related state (e.g., channel state).
+  // mu_call_ controls access to call-related state (e.g., the call lists).
+  //
+  // If they are ever required to be nested, you must lock mu_global_
+  // before mu_call_. This is currently used in shutdown processing
+  // (ShutdownAndNotify() and MaybeFinishShutdown()).
+  Mutex mu_global_;  // mutex for server and channel state
+  Mutex mu_call_;    // mutex for call-specific state
+
+  // startup synchronization: flag is protected by mu_global_, signals whether
+  // we are doing the listener start routine or not.
+  bool starting_ = false;
+  CondVar starting_cv_;
+
+  std::vector<std::unique_ptr<RegisteredMethod>> registered_methods_;
+
+  // Request matcher for unregistered methods.
+  std::unique_ptr<RequestMatcherInterface> unregistered_request_matcher_;
+
+  std::atomic_bool shutdown_flag_{false};
+  bool shutdown_published_ = false;
+  std::vector<ShutdownTag> shutdown_tags_;
+
+  std::list<ChannelData*> channels_;
+
+  std::list<Listener> listeners_;
+  size_t listeners_destroyed_ = 0;
+
+  // The last time we printed a shutdown progress message.
+  gpr_timespec last_shutdown_message_time_;
+};
 
 }  // namespace grpc_core
 
+struct grpc_server {
+  grpc_core::OrphanablePtr<grpc_core::Server> core_server;
+};
+
 #endif /* GRPC_CORE_LIB_SURFACE_SERVER_H */
diff --git a/src/cpp/server/server_cc.cc b/src/cpp/server/server_cc.cc
index 489480bc503..c9aa49c44f6 100644
--- a/src/cpp/server/server_cc.cc
+++ b/src/cpp/server/server_cc.cc
@@ -547,7 +547,7 @@ class Server::CallbackRequest final
   // is nullptr since these services don't have pre-defined methods.
   CallbackRequest(Server* server, grpc::internal::RpcServiceMethod* method,
                   grpc::CompletionQueue* cq,
-                  grpc_core::ServerRegisteredCallAllocation* data)
+                  grpc_core::Server::RegisteredCallAllocation* data)
       : server_(server),
         method_(method),
         has_request_payload_(method->method_type() ==
@@ -568,7 +568,7 @@ class Server::CallbackRequest final
   // For generic services, method is nullptr since these services don't have
   // pre-defined methods.
   CallbackRequest(Server* server, grpc::CompletionQueue* cq,
-                  grpc_core::ServerBatchCallAllocation* data)
+                  grpc_core::Server::BatchCallAllocation* data)
       : server_(server),
         method_(nullptr),
         has_request_payload_(false),
@@ -1063,9 +1063,9 @@ bool Server::RegisterService(const std::string* host, grpc::Service* service) {
       has_callback_methods_ = true;
       grpc::internal::RpcServiceMethod* method_value = method.get();
       grpc::CompletionQueue* cq = CallbackCQ();
-      grpc_core::SetServerRegisteredMethodAllocator(
-          server_, cq->cq(), method_registration_tag, [this, cq, method_value] {
-            grpc_core::ServerRegisteredCallAllocation result;
+      server_->core_server->SetRegisteredMethodAllocator(
+          cq->cq(), method_registration_tag, [this, cq, method_value] {
+            grpc_core::Server::RegisteredCallAllocation result;
             new CallbackRequest<grpc::CallbackServerContext>(this, method_value,
                                                              cq, &result);
             return result;
@@ -1104,8 +1104,8 @@ void Server::RegisterCallbackGenericService(
   generic_handler_.reset(service->Handler());
 
   grpc::CompletionQueue* cq = CallbackCQ();
-  grpc_core::SetServerBatchMethodAllocator(server_, cq->cq(), [this, cq] {
-    grpc_core::ServerBatchCallAllocation result;
+  server_->core_server->SetBatchMethodAllocator(cq->cq(), [this, cq] {
+    grpc_core::Server::BatchCallAllocation result;
     new CallbackRequest<grpc::GenericCallbackServerContext>(this, cq, &result);
     return result;
   });
diff --git a/test/core/bad_client/bad_client.cc b/test/core/bad_client/bad_client.cc
index ba402f2a3fe..1f366758fd4 100644
--- a/test/core/bad_client/bad_client.cc
+++ b/test/core/bad_client/bad_client.cc
@@ -65,8 +65,8 @@ static void set_done_write(void* arg, grpc_error* /*error*/) {
 static void server_setup_transport(void* ts, grpc_transport* transport) {
   thd_args* a = static_cast<thd_args*>(ts);
   grpc_core::ExecCtx exec_ctx;
-  grpc_server_setup_transport(a->server, transport, nullptr,
-                              grpc_server_get_channel_args(a->server), nullptr);
+  a->server->core_server->SetupTransport(
+      transport, nullptr, a->server->core_server->channel_args(), nullptr);
 }
 
 /* Sets the read_done event */
@@ -219,7 +219,7 @@ void grpc_run_bad_client_test(
   grpc_endpoint_add_to_pollset(sfd.server, grpc_cq_pollset(a.cq));
 
   /* Check a ground truth */
-  GPR_ASSERT(grpc_server_has_open_connections(a.server));
+  GPR_ASSERT(a.server->core_server->HasOpenConnections());
 
   gpr_event_init(&a.done_thd);
   a.validator = server_validator;
diff --git a/test/core/bad_client/tests/bad_streaming_id.cc b/test/core/bad_client/tests/bad_streaming_id.cc
index b63fccd8d25..3efc3ebdb12 100644
--- a/test/core/bad_client/tests/bad_streaming_id.cc
+++ b/test/core/bad_client/tests/bad_streaming_id.cc
@@ -76,7 +76,7 @@ namespace {
 
 void verifier(grpc_server* server, grpc_completion_queue* cq,
               void* /*registered_method*/) {
-  while (grpc_server_has_open_connections(server)) {
+  while (server->core_server->HasOpenConnections()) {
     GPR_ASSERT(grpc_completion_queue_next(
                    cq, grpc_timeout_milliseconds_to_deadline(20), nullptr)
                    .type == GRPC_QUEUE_TIMEOUT);
diff --git a/test/core/bad_client/tests/badreq.cc b/test/core/bad_client/tests/badreq.cc
index cd6ed70c8cc..ab322b292b0 100644
--- a/test/core/bad_client/tests/badreq.cc
+++ b/test/core/bad_client/tests/badreq.cc
@@ -31,7 +31,7 @@
 
 static void verifier(grpc_server* server, grpc_completion_queue* cq,
                      void* /*registered_method*/) {
-  while (grpc_server_has_open_connections(server)) {
+  while (server->core_server->HasOpenConnections()) {
     GPR_ASSERT(grpc_completion_queue_next(
                    cq, grpc_timeout_milliseconds_to_deadline(20), nullptr)
                    .type == GRPC_QUEUE_TIMEOUT);
diff --git a/test/core/bad_client/tests/connection_prefix.cc b/test/core/bad_client/tests/connection_prefix.cc
index b0b6ca67af4..45473722512 100644
--- a/test/core/bad_client/tests/connection_prefix.cc
+++ b/test/core/bad_client/tests/connection_prefix.cc
@@ -21,7 +21,7 @@
 
 static void verifier(grpc_server* server, grpc_completion_queue* cq,
                      void* /*registered_method*/) {
-  while (grpc_server_has_open_connections(server)) {
+  while (server->core_server->HasOpenConnections()) {
     GPR_ASSERT(grpc_completion_queue_next(
                    cq, grpc_timeout_milliseconds_to_deadline(20), nullptr)
                    .type == GRPC_QUEUE_TIMEOUT);
diff --git a/test/core/bad_client/tests/headers.cc b/test/core/bad_client/tests/headers.cc
index ed6b533e230..b0795dd015b 100644
--- a/test/core/bad_client/tests/headers.cc
+++ b/test/core/bad_client/tests/headers.cc
@@ -25,7 +25,7 @@
 
 static void verifier(grpc_server* server, grpc_completion_queue* cq,
                      void* /*registered_method*/) {
-  while (grpc_server_has_open_connections(server)) {
+  while (server->core_server->HasOpenConnections()) {
     GPR_ASSERT(grpc_completion_queue_next(
                    cq, grpc_timeout_milliseconds_to_deadline(20), nullptr)
                    .type == GRPC_QUEUE_TIMEOUT);
diff --git a/test/core/bad_client/tests/initial_settings_frame.cc b/test/core/bad_client/tests/initial_settings_frame.cc
index 204bf735bfe..0deda7025b1 100644
--- a/test/core/bad_client/tests/initial_settings_frame.cc
+++ b/test/core/bad_client/tests/initial_settings_frame.cc
@@ -24,7 +24,7 @@
 
 static void verifier(grpc_server* server, grpc_completion_queue* cq,
                      void* /*registered_method*/) {
-  while (grpc_server_has_open_connections(server)) {
+  while (server->core_server->HasOpenConnections()) {
     GPR_ASSERT(grpc_completion_queue_next(
                    cq, grpc_timeout_milliseconds_to_deadline(20), nullptr)
                    .type == GRPC_QUEUE_TIMEOUT);
diff --git a/test/core/bad_client/tests/out_of_bounds.cc b/test/core/bad_client/tests/out_of_bounds.cc
index 1e5c8738f13..0e38223f288 100644
--- a/test/core/bad_client/tests/out_of_bounds.cc
+++ b/test/core/bad_client/tests/out_of_bounds.cc
@@ -31,7 +31,7 @@ namespace {
 
 void verifier(grpc_server* server, grpc_completion_queue* cq,
               void* /*registered_method*/) {
-  while (grpc_server_has_open_connections(server)) {
+  while (server->core_server->HasOpenConnections()) {
     GPR_ASSERT(grpc_completion_queue_next(
                    cq, grpc_timeout_milliseconds_to_deadline(20), nullptr)
                    .type == GRPC_QUEUE_TIMEOUT);
diff --git a/test/core/bad_client/tests/server_registered_method.cc b/test/core/bad_client/tests/server_registered_method.cc
index e700cb490c0..da0ac28d53b 100644
--- a/test/core/bad_client/tests/server_registered_method.cc
+++ b/test/core/bad_client/tests/server_registered_method.cc
@@ -68,7 +68,7 @@ static void verifier_succeeds(grpc_server* server, grpc_completion_queue* cq,
 
 static void verifier_fails(grpc_server* server, grpc_completion_queue* cq,
                            void* /*registered_method*/) {
-  while (grpc_server_has_open_connections(server)) {
+  while (server->core_server->HasOpenConnections()) {
     GPR_ASSERT(grpc_completion_queue_next(
                    cq, grpc_timeout_milliseconds_to_deadline(20), nullptr)
                    .type == GRPC_QUEUE_TIMEOUT);
diff --git a/test/core/bad_client/tests/simple_request.cc b/test/core/bad_client/tests/simple_request.cc
index eace174a107..d1df05dd349 100644
--- a/test/core/bad_client/tests/simple_request.cc
+++ b/test/core/bad_client/tests/simple_request.cc
@@ -115,7 +115,7 @@ static void verifier(grpc_server* server, grpc_completion_queue* cq,
 
 static void failure_verifier(grpc_server* server, grpc_completion_queue* cq,
                              void* /*registered_method*/) {
-  while (grpc_server_has_open_connections(server)) {
+  while (server->core_server->HasOpenConnections()) {
     GPR_ASSERT(grpc_completion_queue_next(
                    cq, grpc_timeout_milliseconds_to_deadline(20), nullptr)
                    .type == GRPC_QUEUE_TIMEOUT);
diff --git a/test/core/bad_client/tests/unknown_frame.cc b/test/core/bad_client/tests/unknown_frame.cc
index 1b42a7cb73f..645c08f6588 100644
--- a/test/core/bad_client/tests/unknown_frame.cc
+++ b/test/core/bad_client/tests/unknown_frame.cc
@@ -26,7 +26,7 @@
 
 static void verifier(grpc_server* server, grpc_completion_queue* cq,
                      void* /*registered_method*/) {
-  while (grpc_server_has_open_connections(server)) {
+  while (server->core_server->HasOpenConnections()) {
     GPR_ASSERT(grpc_completion_queue_next(
                    cq, grpc_timeout_milliseconds_to_deadline(20), nullptr)
                    .type == GRPC_QUEUE_TIMEOUT);
diff --git a/test/core/bad_client/tests/window_overflow.cc b/test/core/bad_client/tests/window_overflow.cc
index 32afd15992c..dbb98a2b0d8 100644
--- a/test/core/bad_client/tests/window_overflow.cc
+++ b/test/core/bad_client/tests/window_overflow.cc
@@ -44,7 +44,7 @@
 
 static void verifier(grpc_server* server, grpc_completion_queue* cq,
                      void* /*registered_method*/) {
-  while (grpc_server_has_open_connections(server)) {
+  while (server->core_server->HasOpenConnections()) {
     GPR_ASSERT(grpc_completion_queue_next(
                    cq, grpc_timeout_milliseconds_to_deadline(20), nullptr)
                    .type == GRPC_QUEUE_TIMEOUT);
diff --git a/test/core/bad_connection/close_fd_test.cc b/test/core/bad_connection/close_fd_test.cc
index 380cbcd8dfa..05498f33521 100644
--- a/test/core/bad_connection/close_fd_test.cc
+++ b/test/core/bad_connection/close_fd_test.cc
@@ -73,9 +73,8 @@ static test_ctx g_ctx;
 static void server_setup_transport(grpc_transport* transport) {
   grpc_core::ExecCtx exec_ctx;
   grpc_endpoint_add_to_pollset(g_ctx.ep->server, grpc_cq_pollset(g_ctx.cq));
-  grpc_server_setup_transport(g_ctx.server, transport, nullptr,
-                              grpc_server_get_channel_args(g_ctx.server),
-                              nullptr);
+  g_ctx.server->core_server->SetupTransport(
+      transport, nullptr, g_ctx.server->core_server->channel_args(), nullptr);
 }
 
 static void client_setup_transport(grpc_transport* transport) {
diff --git a/test/core/channel/channelz_test.cc b/test/core/channel/channelz_test.cc
index 338cd0f7ee6..1a281257aeb 100644
--- a/test/core/channel/channelz_test.cc
+++ b/test/core/channel/channelz_test.cc
@@ -519,7 +519,7 @@ TEST_F(ChannelzRegistryBasedTest, InternalChannelTest) {
 TEST(ChannelzServerTest, BasicServerAPIFunctionality) {
   grpc_core::ExecCtx exec_ctx;
   ServerFixture server(10);
-  ServerNode* channelz_server = grpc_server_get_channelz_node(server.server());
+  ServerNode* channelz_server = server.server()->core_server->channelz_node();
   channelz_server->RecordCallStarted();
   channelz_server->RecordCallFailed();
   channelz_server->RecordCallSucceeded();
diff --git a/test/core/end2end/fixtures/h2_sockpair+trace.cc b/test/core/end2end/fixtures/h2_sockpair+trace.cc
index a3afee46424..fbecb7ec4bc 100644
--- a/test/core/end2end/fixtures/h2_sockpair+trace.cc
+++ b/test/core/end2end/fixtures/h2_sockpair+trace.cc
@@ -52,8 +52,8 @@ static void server_setup_transport(void* ts, grpc_transport* transport) {
   grpc_core::ExecCtx exec_ctx;
   grpc_endpoint_pair* sfd = static_cast<grpc_endpoint_pair*>(f->fixture_data);
   grpc_endpoint_add_to_pollset(sfd->server, grpc_cq_pollset(f->cq));
-  grpc_server_setup_transport(f->server, transport, nullptr,
-                              grpc_server_get_channel_args(f->server), nullptr);
+  f->server->core_server->SetupTransport(
+      transport, nullptr, f->server->core_server->channel_args(), nullptr);
 }
 
 typedef struct {
diff --git a/test/core/end2end/fixtures/h2_sockpair.cc b/test/core/end2end/fixtures/h2_sockpair.cc
index 637bb0c0dee..8ae50a7e053 100644
--- a/test/core/end2end/fixtures/h2_sockpair.cc
+++ b/test/core/end2end/fixtures/h2_sockpair.cc
@@ -46,8 +46,8 @@ static void server_setup_transport(void* ts, grpc_transport* transport) {
   grpc_core::ExecCtx exec_ctx;
   grpc_endpoint_pair* sfd = static_cast<grpc_endpoint_pair*>(f->fixture_data);
   grpc_endpoint_add_to_pollset(sfd->server, grpc_cq_pollset(f->cq));
-  grpc_server_setup_transport(f->server, transport, nullptr,
-                              grpc_server_get_channel_args(f->server), nullptr);
+  f->server->core_server->SetupTransport(
+      transport, nullptr, f->server->core_server->channel_args(), nullptr);
 }
 
 typedef struct {
diff --git a/test/core/end2end/fixtures/h2_sockpair_1byte.cc b/test/core/end2end/fixtures/h2_sockpair_1byte.cc
index 1b9a3a4051b..1f13711a57c 100644
--- a/test/core/end2end/fixtures/h2_sockpair_1byte.cc
+++ b/test/core/end2end/fixtures/h2_sockpair_1byte.cc
@@ -46,8 +46,8 @@ static void server_setup_transport(void* ts, grpc_transport* transport) {
   grpc_core::ExecCtx exec_ctx;
   grpc_endpoint_pair* sfd = static_cast<grpc_endpoint_pair*>(f->fixture_data);
   grpc_endpoint_add_to_pollset(sfd->server, grpc_cq_pollset(f->cq));
-  grpc_server_setup_transport(f->server, transport, nullptr,
-                              grpc_server_get_channel_args(f->server), nullptr);
+  f->server->core_server->SetupTransport(
+      transport, nullptr, f->server->core_server->channel_args(), nullptr);
 }
 
 typedef struct {
diff --git a/test/core/end2end/fuzzers/server_fuzzer.cc b/test/core/end2end/fuzzers/server_fuzzer.cc
index 1fabd8ca172..e811175c329 100644
--- a/test/core/end2end/fuzzers/server_fuzzer.cc
+++ b/test/core/end2end/fuzzers/server_fuzzer.cc
@@ -58,7 +58,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
     grpc_server_start(server);
     grpc_transport* transport =
         grpc_create_chttp2_transport(nullptr, mock_endpoint, false);
-    grpc_server_setup_transport(server, transport, nullptr, nullptr, nullptr);
+    server->core_server->SetupTransport(transport, nullptr, nullptr, nullptr);
     grpc_chttp2_transport_start_reading(transport, nullptr, nullptr);
 
     grpc_call* call1 = nullptr;
diff --git a/test/core/end2end/tests/channelz.cc b/test/core/end2end/tests/channelz.cc
index fe50d9a6a5c..1be992dc0e8 100644
--- a/test/core/end2end/tests/channelz.cc
+++ b/test/core/end2end/tests/channelz.cc
@@ -214,7 +214,7 @@ static void test_channelz(grpc_end2end_test_config config) {
   GPR_ASSERT(channelz_channel != nullptr);
 
   grpc_core::channelz::ServerNode* channelz_server =
-      grpc_server_get_channelz_node(f.server);
+      f.server->core_server->channelz_node();
   GPR_ASSERT(channelz_server != nullptr);
 
   std::string json = channelz_channel->RenderJsonString();
@@ -275,7 +275,7 @@ static void test_channelz_with_channel_trace(grpc_end2end_test_config config) {
   GPR_ASSERT(channelz_channel != nullptr);
 
   grpc_core::channelz::ServerNode* channelz_server =
-      grpc_server_get_channelz_node(f.server);
+      f.server->core_server->channelz_node();
   GPR_ASSERT(channelz_server != nullptr);
 
   run_one_request(config, f, true);
diff --git a/test/cpp/microbenchmarks/fullstack_fixtures.h b/test/cpp/microbenchmarks/fullstack_fixtures.h
index 0f75314ea0e..73b4e41900e 100644
--- a/test/cpp/microbenchmarks/fullstack_fixtures.h
+++ b/test/cpp/microbenchmarks/fullstack_fixtures.h
@@ -174,17 +174,17 @@ class EndpointPairFixture : public BaseFixture {
      * */
     {
       const grpc_channel_args* server_args =
-          grpc_server_get_channel_args(server_->c_server());
+          server_->c_server()->core_server->channel_args();
       server_transport_ = grpc_create_chttp2_transport(
           server_args, endpoints.server, false /* is_client */);
 
       for (grpc_pollset* pollset :
-           grpc_server_get_pollsets(server_->c_server())) {
+           server_->c_server()->core_server->pollsets()) {
         grpc_endpoint_add_to_pollset(endpoints.server, pollset);
       }
 
-      grpc_server_setup_transport(server_->c_server(), server_transport_,
-                                  nullptr, server_args, nullptr);
+      server_->c_server()->core_server->SetupTransport(
+          server_transport_, nullptr, server_args, nullptr);
       grpc_chttp2_transport_start_reading(server_transport_, nullptr, nullptr);
     }
 
diff --git a/test/cpp/performance/writes_per_rpc_test.cc b/test/cpp/performance/writes_per_rpc_test.cc
index 9488cc6799b..e262078e149 100644
--- a/test/cpp/performance/writes_per_rpc_test.cc
+++ b/test/cpp/performance/writes_per_rpc_test.cc
@@ -71,17 +71,17 @@ class EndpointPairFixture {
     /* add server endpoint to server_ */
     {
       const grpc_channel_args* server_args =
-          grpc_server_get_channel_args(server_->c_server());
+          server_->c_server()->core_server->channel_args();
       grpc_transport* transport = grpc_create_chttp2_transport(
           server_args, endpoints.server, false /* is_client */);
 
       for (grpc_pollset* pollset :
-           grpc_server_get_pollsets(server_->c_server())) {
+           server_->c_server()->core_server->pollsets()) {
         grpc_endpoint_add_to_pollset(endpoints.server, pollset);
       }
 
-      grpc_server_setup_transport(server_->c_server(), transport, nullptr,
-                                  server_args, nullptr);
+      server_->c_server()->core_server->SetupTransport(transport, nullptr,
+                                                       server_args, nullptr);
       grpc_chttp2_transport_start_reading(transport, nullptr, nullptr);
     }
 

From 6d7d35fa3968f00bc252e5314b962a9a3b44c67f Mon Sep 17 00:00:00 2001
From: Esun Kim <veblush@google.com>
Date: Mon, 3 Aug 2020 14:25:24 -0700
Subject: [PATCH 35/38] Squashed 'third_party/upb/' changes from
 92e63da733..382d5afc60

382d5afc60 Merge pull request #306 from haberman/bigendian
efefbffc80 Fixed binary encoding and decoding for big-endian machines.
5d3083013c Merge pull request #304 from haberman/upb-assume
55dd9d3e41 Fixed UPB_ASSUME() for non-GCC, non-MSVC platforms.
e4c8afd0d4 Merge pull request #303 from haberman/packed-def
8284321780 Fixed upb_fielddef_packed() to have the correct default.
ed86d98f53 Merge pull request #302 from haberman/verify-utf8
8e26a33bcb Added a test for UTF-8 parse checking and added missing error reporting.
2c666bc8f6 Use C-style comment instead of C++.
a77ea639d5 Verify UTF-8 when parsing proto3 string fields.
7f19072206 Merge pull request #301 from haberman/rm-pop
bfdfe5a914 Removed unused push/pop functions.
1479f2d7ca Merge pull request #299 from haberman/google3-fixes
8f11ec57d2 Applied changes from google3.
7172b981dd Merge pull request #297 from haberman/bugfixes
ffe40c9f94 Only run Valgrind if it is present.
922c126b8b Fixed unit test errors that were causing Valgrind failures.
086a68d191 Fixed memory leak that could occur after upb_arena_fuse().
35abcc248b Added test that should trigger a memory leak.
7d726c8da6 JSON parser: Bugfix for float/double in quotes.
74c60c757b Merge pull request #296 from haberman/php-fixes
efe11c6c50 Removed excess logging statement.
81c2aa753e Fixes for the PHP C Extension.

git-subtree-dir: third_party/upb
git-subtree-split: 382d5afc60e05470c23e8de19b19fc5ad231e732
---
 BUILD                                         | 34 ++++++++
 bazel/upb_proto_library.bzl                   |  3 +
 .../google/protobuf/descriptor.upb.c          | 72 ++++++++---------
 kokoro/ubuntu/build.sh                        |  5 ++
 tests/bindings/lua/test_upb.lua               | 17 ++++
 tests/test_cpp.cc                             | 17 +++-
 tests/test_generated_code.c                   |  4 +-
 upb/decode.c                                  | 79 +++++++++++++++----
 upb/def.c                                     | 47 +++++++++--
 upb/encode.c                                  |  4 +-
 upb/handlers.h                                |  6 +-
 upb/json_decode.c                             |  1 +
 upb/json_encode.c                             | 19 ++++-
 upb/msg.h                                     |  4 +-
 upb/port_def.inc                              |  2 +-
 upb/table.c                                   | 11 ---
 upb/table.int.h                               |  9 ---
 upb/upb.c                                     | 30 +++----
 upb/upb.h                                     | 26 ++++++
 upb/upb.hpp                                   |  2 +
 upbc/generator.cc                             | 22 +++++-
 21 files changed, 304 insertions(+), 110 deletions(-)

diff --git a/BUILD b/BUILD
index cfc734a9b4e..90aa7a79194 100644
--- a/BUILD
+++ b/BUILD
@@ -735,6 +735,33 @@ cc_library(
     }),
 )
 
+upb_amalgamation(
+    name = "gen_ruby_amalgamation",
+    prefix = "ruby-",
+    outs = [
+        "ruby-upb.c",
+        "ruby-upb.h",
+    ],
+    amalgamator = ":amalgamate",
+    libs = [
+        ":upb",
+        ":descriptor_upb_proto",
+        ":reflection",
+        ":port",
+        ":json",
+    ],
+)
+
+cc_library(
+    name = "ruby_amalgamation",
+    srcs = ["ruby-upb.c"],
+    hdrs = ["ruby-upb.h"],
+    copts = select({
+        ":windows": [],
+        "//conditions:default": COPTS,
+    }),
+)
+
 # Lua ##########################################################################
 
 cc_library(
@@ -765,6 +792,7 @@ cc_test(
         "upb/bindings/lua/upb.lua",
         ":descriptor_proto_lua",
         ":test_messages_proto3_proto_lua",
+        ":test_messages_proto2_proto_lua",
         ":test_proto_lua",
         "@com_google_protobuf//:conformance_proto",
         "@com_google_protobuf//:descriptor_proto",
@@ -807,6 +835,12 @@ lua_proto_library(
     deps = ["@com_google_protobuf//:test_messages_proto3_proto"],
 )
 
+lua_proto_library(
+    name = "test_messages_proto2_proto_lua",
+    testonly = 1,
+    deps = ["@com_google_protobuf//:test_messages_proto2_proto"],
+)
+
 # Test the CMake build #########################################################
 
 filegroup(
diff --git a/bazel/upb_proto_library.bzl b/bazel/upb_proto_library.bzl
index 4dce00826dc..732239b7a5e 100644
--- a/bazel/upb_proto_library.bzl
+++ b/bazel/upb_proto_library.bzl
@@ -120,6 +120,9 @@ _WrappedGeneratedSrcsInfo = provider(fields = ["srcs"])
 _WrappedDefsGeneratedSrcsInfo = provider(fields = ["srcs"])
 
 def _compile_upb_protos(ctx, proto_info, proto_sources, ext):
+    if len(proto_sources) == 0:
+        return GeneratedSrcsInfo(srcs = [], hdrs = [])
+
     srcs = [_generate_output_file(ctx, name, ext + ".c") for name in proto_sources]
     hdrs = [_generate_output_file(ctx, name, ext + ".h") for name in proto_sources]
     transitive_sets = proto_info.transitive_descriptor_sets.to_list()
diff --git a/generated_for_cmake/google/protobuf/descriptor.upb.c b/generated_for_cmake/google/protobuf/descriptor.upb.c
index 3c7363922e9..44cd3ae2cec 100644
--- a/generated_for_cmake/google/protobuf/descriptor.upb.c
+++ b/generated_for_cmake/google/protobuf/descriptor.upb.c
@@ -36,9 +36,9 @@ static const upb_msglayout *const google_protobuf_FileDescriptorProto_submsgs[6]
 };
 
 static const upb_msglayout_field google_protobuf_FileDescriptorProto__fields[12] = {
-  {1, UPB_SIZE(4, 8), 1, 0, 9, 1},
-  {2, UPB_SIZE(12, 24), 2, 0, 9, 1},
-  {3, UPB_SIZE(36, 72), 0, 0, 9, 3},
+  {1, UPB_SIZE(4, 8), 1, 0, 12, 1},
+  {2, UPB_SIZE(12, 24), 2, 0, 12, 1},
+  {3, UPB_SIZE(36, 72), 0, 0, 12, 3},
   {4, UPB_SIZE(40, 80), 0, 0, 11, 3},
   {5, UPB_SIZE(44, 88), 0, 1, 11, 3},
   {6, UPB_SIZE(48, 96), 0, 4, 11, 3},
@@ -47,7 +47,7 @@ static const upb_msglayout_field google_protobuf_FileDescriptorProto__fields[12]
   {9, UPB_SIZE(32, 64), 5, 5, 11, 1},
   {10, UPB_SIZE(56, 112), 0, 0, 5, 3},
   {11, UPB_SIZE(60, 120), 0, 0, 5, 3},
-  {12, UPB_SIZE(20, 40), 3, 0, 9, 1},
+  {12, UPB_SIZE(20, 40), 3, 0, 12, 1},
 };
 
 const upb_msglayout google_protobuf_FileDescriptorProto_msginit = {
@@ -67,7 +67,7 @@ static const upb_msglayout *const google_protobuf_DescriptorProto_submsgs[8] = {
 };
 
 static const upb_msglayout_field google_protobuf_DescriptorProto__fields[10] = {
-  {1, UPB_SIZE(4, 8), 1, 0, 9, 1},
+  {1, UPB_SIZE(4, 8), 1, 0, 12, 1},
   {2, UPB_SIZE(16, 32), 0, 4, 11, 3},
   {3, UPB_SIZE(20, 40), 0, 0, 11, 3},
   {4, UPB_SIZE(24, 48), 0, 3, 11, 3},
@@ -76,7 +76,7 @@ static const upb_msglayout_field google_protobuf_DescriptorProto__fields[10] = {
   {7, UPB_SIZE(12, 24), 2, 5, 11, 1},
   {8, UPB_SIZE(36, 72), 0, 6, 11, 3},
   {9, UPB_SIZE(40, 80), 0, 2, 11, 3},
-  {10, UPB_SIZE(44, 88), 0, 0, 9, 3},
+  {10, UPB_SIZE(44, 88), 0, 0, 12, 3},
 };
 
 const upb_msglayout google_protobuf_DescriptorProto_msginit = {
@@ -131,16 +131,16 @@ static const upb_msglayout *const google_protobuf_FieldDescriptorProto_submsgs[1
 };
 
 static const upb_msglayout_field google_protobuf_FieldDescriptorProto__fields[11] = {
-  {1, UPB_SIZE(36, 40), 6, 0, 9, 1},
-  {2, UPB_SIZE(44, 56), 7, 0, 9, 1},
+  {1, UPB_SIZE(36, 40), 6, 0, 12, 1},
+  {2, UPB_SIZE(44, 56), 7, 0, 12, 1},
   {3, UPB_SIZE(24, 24), 3, 0, 5, 1},
   {4, UPB_SIZE(8, 8), 1, 0, 14, 1},
   {5, UPB_SIZE(16, 16), 2, 0, 14, 1},
-  {6, UPB_SIZE(52, 72), 8, 0, 9, 1},
-  {7, UPB_SIZE(60, 88), 9, 0, 9, 1},
+  {6, UPB_SIZE(52, 72), 8, 0, 12, 1},
+  {7, UPB_SIZE(60, 88), 9, 0, 12, 1},
   {8, UPB_SIZE(76, 120), 11, 0, 11, 1},
   {9, UPB_SIZE(28, 28), 4, 0, 5, 1},
-  {10, UPB_SIZE(68, 104), 10, 0, 9, 1},
+  {10, UPB_SIZE(68, 104), 10, 0, 12, 1},
   {17, UPB_SIZE(32, 32), 5, 0, 8, 1},
 };
 
@@ -155,7 +155,7 @@ static const upb_msglayout *const google_protobuf_OneofDescriptorProto_submsgs[1
 };
 
 static const upb_msglayout_field google_protobuf_OneofDescriptorProto__fields[2] = {
-  {1, UPB_SIZE(4, 8), 1, 0, 9, 1},
+  {1, UPB_SIZE(4, 8), 1, 0, 12, 1},
   {2, UPB_SIZE(12, 24), 2, 0, 11, 1},
 };
 
@@ -172,11 +172,11 @@ static const upb_msglayout *const google_protobuf_EnumDescriptorProto_submsgs[3]
 };
 
 static const upb_msglayout_field google_protobuf_EnumDescriptorProto__fields[5] = {
-  {1, UPB_SIZE(4, 8), 1, 0, 9, 1},
+  {1, UPB_SIZE(4, 8), 1, 0, 12, 1},
   {2, UPB_SIZE(16, 32), 0, 2, 11, 3},
   {3, UPB_SIZE(12, 24), 2, 1, 11, 1},
   {4, UPB_SIZE(20, 40), 0, 0, 11, 3},
-  {5, UPB_SIZE(24, 48), 0, 0, 9, 3},
+  {5, UPB_SIZE(24, 48), 0, 0, 12, 3},
 };
 
 const upb_msglayout google_protobuf_EnumDescriptorProto_msginit = {
@@ -201,7 +201,7 @@ static const upb_msglayout *const google_protobuf_EnumValueDescriptorProto_subms
 };
 
 static const upb_msglayout_field google_protobuf_EnumValueDescriptorProto__fields[3] = {
-  {1, UPB_SIZE(8, 8), 2, 0, 9, 1},
+  {1, UPB_SIZE(8, 8), 2, 0, 12, 1},
   {2, UPB_SIZE(4, 4), 1, 0, 5, 1},
   {3, UPB_SIZE(16, 24), 3, 0, 11, 1},
 };
@@ -218,7 +218,7 @@ static const upb_msglayout *const google_protobuf_ServiceDescriptorProto_submsgs
 };
 
 static const upb_msglayout_field google_protobuf_ServiceDescriptorProto__fields[3] = {
-  {1, UPB_SIZE(4, 8), 1, 0, 9, 1},
+  {1, UPB_SIZE(4, 8), 1, 0, 12, 1},
   {2, UPB_SIZE(16, 32), 0, 0, 11, 3},
   {3, UPB_SIZE(12, 24), 2, 1, 11, 1},
 };
@@ -234,9 +234,9 @@ static const upb_msglayout *const google_protobuf_MethodDescriptorProto_submsgs[
 };
 
 static const upb_msglayout_field google_protobuf_MethodDescriptorProto__fields[6] = {
-  {1, UPB_SIZE(4, 8), 3, 0, 9, 1},
-  {2, UPB_SIZE(12, 24), 4, 0, 9, 1},
-  {3, UPB_SIZE(20, 40), 5, 0, 9, 1},
+  {1, UPB_SIZE(4, 8), 3, 0, 12, 1},
+  {2, UPB_SIZE(12, 24), 4, 0, 12, 1},
+  {3, UPB_SIZE(20, 40), 5, 0, 12, 1},
   {4, UPB_SIZE(28, 56), 6, 0, 11, 1},
   {5, UPB_SIZE(1, 1), 1, 0, 8, 1},
   {6, UPB_SIZE(2, 2), 2, 0, 8, 1},
@@ -253,11 +253,11 @@ static const upb_msglayout *const google_protobuf_FileOptions_submsgs[1] = {
 };
 
 static const upb_msglayout_field google_protobuf_FileOptions__fields[21] = {
-  {1, UPB_SIZE(28, 32), 11, 0, 9, 1},
-  {8, UPB_SIZE(36, 48), 12, 0, 9, 1},
+  {1, UPB_SIZE(28, 32), 11, 0, 12, 1},
+  {8, UPB_SIZE(36, 48), 12, 0, 12, 1},
   {9, UPB_SIZE(8, 8), 1, 0, 14, 1},
   {10, UPB_SIZE(16, 16), 2, 0, 8, 1},
-  {11, UPB_SIZE(44, 64), 13, 0, 9, 1},
+  {11, UPB_SIZE(44, 64), 13, 0, 12, 1},
   {16, UPB_SIZE(17, 17), 3, 0, 8, 1},
   {17, UPB_SIZE(18, 18), 4, 0, 8, 1},
   {18, UPB_SIZE(19, 19), 5, 0, 8, 1},
@@ -265,14 +265,14 @@ static const upb_msglayout_field google_protobuf_FileOptions__fields[21] = {
   {23, UPB_SIZE(21, 21), 7, 0, 8, 1},
   {27, UPB_SIZE(22, 22), 8, 0, 8, 1},
   {31, UPB_SIZE(23, 23), 9, 0, 8, 1},
-  {36, UPB_SIZE(52, 80), 14, 0, 9, 1},
-  {37, UPB_SIZE(60, 96), 15, 0, 9, 1},
-  {39, UPB_SIZE(68, 112), 16, 0, 9, 1},
-  {40, UPB_SIZE(76, 128), 17, 0, 9, 1},
-  {41, UPB_SIZE(84, 144), 18, 0, 9, 1},
+  {36, UPB_SIZE(52, 80), 14, 0, 12, 1},
+  {37, UPB_SIZE(60, 96), 15, 0, 12, 1},
+  {39, UPB_SIZE(68, 112), 16, 0, 12, 1},
+  {40, UPB_SIZE(76, 128), 17, 0, 12, 1},
+  {41, UPB_SIZE(84, 144), 18, 0, 12, 1},
   {42, UPB_SIZE(24, 24), 10, 0, 8, 1},
-  {44, UPB_SIZE(92, 160), 19, 0, 9, 1},
-  {45, UPB_SIZE(100, 176), 20, 0, 9, 1},
+  {44, UPB_SIZE(92, 160), 19, 0, 12, 1},
+  {45, UPB_SIZE(100, 176), 20, 0, 12, 1},
   {999, UPB_SIZE(108, 192), 0, 0, 11, 3},
 };
 
@@ -402,12 +402,12 @@ static const upb_msglayout *const google_protobuf_UninterpretedOption_submsgs[1]
 
 static const upb_msglayout_field google_protobuf_UninterpretedOption__fields[7] = {
   {2, UPB_SIZE(56, 80), 0, 0, 11, 3},
-  {3, UPB_SIZE(32, 32), 4, 0, 9, 1},
+  {3, UPB_SIZE(32, 32), 4, 0, 12, 1},
   {4, UPB_SIZE(8, 8), 1, 0, 4, 1},
   {5, UPB_SIZE(16, 16), 2, 0, 3, 1},
   {6, UPB_SIZE(24, 24), 3, 0, 1, 1},
   {7, UPB_SIZE(40, 48), 5, 0, 12, 1},
-  {8, UPB_SIZE(48, 64), 6, 0, 9, 1},
+  {8, UPB_SIZE(48, 64), 6, 0, 12, 1},
 };
 
 const upb_msglayout google_protobuf_UninterpretedOption_msginit = {
@@ -417,7 +417,7 @@ const upb_msglayout google_protobuf_UninterpretedOption_msginit = {
 };
 
 static const upb_msglayout_field google_protobuf_UninterpretedOption_NamePart__fields[2] = {
-  {1, UPB_SIZE(4, 8), 2, 0, 9, 2},
+  {1, UPB_SIZE(4, 8), 2, 0, 12, 2},
   {2, UPB_SIZE(1, 1), 1, 0, 8, 2},
 };
 
@@ -444,9 +444,9 @@ const upb_msglayout google_protobuf_SourceCodeInfo_msginit = {
 static const upb_msglayout_field google_protobuf_SourceCodeInfo_Location__fields[5] = {
   {1, UPB_SIZE(20, 40), 0, 0, 5, _UPB_LABEL_PACKED},
   {2, UPB_SIZE(24, 48), 0, 0, 5, _UPB_LABEL_PACKED},
-  {3, UPB_SIZE(4, 8), 1, 0, 9, 1},
-  {4, UPB_SIZE(12, 24), 2, 0, 9, 1},
-  {6, UPB_SIZE(28, 56), 0, 0, 9, 3},
+  {3, UPB_SIZE(4, 8), 1, 0, 12, 1},
+  {4, UPB_SIZE(12, 24), 2, 0, 12, 1},
+  {6, UPB_SIZE(28, 56), 0, 0, 12, 3},
 };
 
 const upb_msglayout google_protobuf_SourceCodeInfo_Location_msginit = {
@@ -471,7 +471,7 @@ const upb_msglayout google_protobuf_GeneratedCodeInfo_msginit = {
 
 static const upb_msglayout_field google_protobuf_GeneratedCodeInfo_Annotation__fields[4] = {
   {1, UPB_SIZE(20, 32), 0, 0, 5, _UPB_LABEL_PACKED},
-  {2, UPB_SIZE(12, 16), 3, 0, 9, 1},
+  {2, UPB_SIZE(12, 16), 3, 0, 12, 1},
   {3, UPB_SIZE(4, 4), 1, 0, 5, 1},
   {4, UPB_SIZE(8, 8), 2, 0, 5, 1},
 };
diff --git a/kokoro/ubuntu/build.sh b/kokoro/ubuntu/build.sh
index c0261a75886..2174e8aa57d 100644
--- a/kokoro/ubuntu/build.sh
+++ b/kokoro/ubuntu/build.sh
@@ -31,4 +31,9 @@ if [[ $(uname) = "Linux" ]]; then
 
   # For some reason kokoro doesn't have Clang available right now.
   #CC=clang CXX=clang++ bazel test -c dbg --copt=-fsanitize=undefined --copt=-fno-sanitize=function,vptr --linkopt=-fsanitize=undefined --action_env=UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1 -- :all -:test_lua
+
+fi
+
+if which valgrind; then
+  bazel test --run_under='valgrind --leak-check=full --error-exitcode=1' :all -- -:test_conformance_upb -:cmake_build
 fi
diff --git a/tests/bindings/lua/test_upb.lua b/tests/bindings/lua/test_upb.lua
index d1c32b16746..4586b34ba74 100644
--- a/tests/bindings/lua/test_upb.lua
+++ b/tests/bindings/lua/test_upb.lua
@@ -3,6 +3,7 @@ local upb = require "lupb"
 local lunit = require "lunit"
 local upb_test = require "tests.test_pb"
 local test_messages_proto3 = require "google.protobuf.test_messages_proto3_pb"
+local test_messages_proto2 = require "google.protobuf.test_messages_proto2_pb"
 local descriptor = require "google.protobuf.descriptor_pb"
 
 if _VERSION >= 'Lua 5.2' then
@@ -69,6 +70,22 @@ function test_msg_map()
   assert_equal(12, msg2.map_int32_int32[6])
 end
 
+function test_utf8()
+  local proto2_msg = test_messages_proto2.TestAllTypesProto2()
+  proto2_msg.optional_string = "\xff"
+  local serialized = upb.encode(proto2_msg)
+
+  -- Decoding invalid UTF-8 succeeds in proto2.
+  upb.decode(test_messages_proto2.TestAllTypesProto2, serialized)
+
+  -- Decoding invalid UTF-8 fails in proto2.
+  assert_error_match("Error decoding protobuf", function()
+    upb.decode(test_messages_proto3.TestAllTypesProto3, serialized)
+  end)
+
+  -- TOOD(haberman): should proto3 accessors also check UTF-8 at set time?
+end
+
 function test_string_double_map()
   msg = upb_test.MapTest()
   msg.map_string_double["one"] = 1.0
diff --git a/tests/test_cpp.cc b/tests/test_cpp.cc
index 4ca81e1c353..802b7008f9d 100644
--- a/tests/test_cpp.cc
+++ b/tests/test_cpp.cc
@@ -50,7 +50,7 @@ static const int kExpectedHandlerData = 1232323;
 
 class StringBufTesterBase {
  public:
-  static const int kFieldNumber = 3;
+  static constexpr int kFieldNumber = 3;
 
   StringBufTesterBase() : seen_(false), handler_data_val_(0) {}
 
@@ -286,7 +286,7 @@ class StartMsgTesterBase {
  public:
   // We don't need the FieldDef it will create, but the test harness still
   // requires that we provide one.
-  static const int kFieldNumber = 3;
+  static constexpr int kFieldNumber = 3;
 
   StartMsgTesterBase() : seen_(false), handler_data_val_(0) {}
 
@@ -437,7 +437,7 @@ class StartMsgTesterBoolMethodWithHandlerData : public StartMsgTesterBase {
 
 class Int32ValueTesterBase {
  public:
-  static const int kFieldNumber = 1;
+  static constexpr int kFieldNumber = 1;
 
   Int32ValueTesterBase() : seen_(false), val_(0), handler_data_val_(0) {}
 
@@ -939,6 +939,17 @@ void TestArena() {
     upb_arena_malloc(arena.ptr(), 1000000);
   }
   ASSERT(n == 0);
+
+  {
+    // Test fuse.
+    upb::Arena arena1;
+    upb::Arena arena2;
+
+    arena1.Fuse(arena2);
+
+    upb_arena_malloc(arena1.ptr(), 10000);
+    upb_arena_malloc(arena2.ptr(), 10000);
+  }
 }
 
 extern "C" {
diff --git a/tests/test_generated_code.c b/tests/test_generated_code.c
index 25fbf77ce8d..c6f024a61fd 100644
--- a/tests/test_generated_code.c
+++ b/tests/test_generated_code.c
@@ -70,7 +70,7 @@ static void test_scalars() {
 
 static void check_string_map_empty(
     protobuf_test_messages_proto3_TestAllTypesProto3 *msg) {
-  size_t iter;
+  size_t iter = UPB_MAP_BEGIN;
 
   ASSERT(
       protobuf_test_messages_proto3_TestAllTypesProto3_map_string_string_size(
@@ -212,7 +212,7 @@ static void test_string_map() {
 
 static void check_int32_map_empty(
     protobuf_test_messages_proto3_TestAllTypesProto3 *msg) {
-  size_t iter;
+  size_t iter = UPB_MAP_BEGIN;
 
   ASSERT(
       protobuf_test_messages_proto3_TestAllTypesProto3_map_int32_int32_size(
diff --git a/upb/decode.c b/upb/decode.c
index d1eec474dfd..a9f1cf548a0 100644
--- a/upb/decode.c
+++ b/upb/decode.c
@@ -62,11 +62,13 @@ static const unsigned fixed64_ok = (1 << UPB_DTYPE_DOUBLE) |
                                    (1 << UPB_DTYPE_SFIXED64);
 
 /* Op: an action to be performed for a wire-type/field-type combination. */
-#define OP_SCALAR_LG2(n) (n)
-#define OP_FIXPCK_LG2(n) (n + 4)
-#define OP_VARPCK_LG2(n) (n + 8)
+#define OP_SCALAR_LG2(n) (n)      /* n in [0, 2, 3] => op in [0, 2, 3] */
 #define OP_STRING 4
-#define OP_SUBMSG 5
+#define OP_BYTES 5
+#define OP_SUBMSG 6
+/* Ops above are scalar-only. Repeated fields can use any op.  */
+#define OP_FIXPCK_LG2(n) (n + 5)  /* n in [2, 3] => op in [7, 8] */
+#define OP_VARPCK_LG2(n) (n + 9)  /* n in [0, 2, 3] => op in [9, 11, 12] */
 
 static const int8_t varint_ops[19] = {
     -1,               /* field not found */
@@ -104,7 +106,7 @@ static const int8_t delim_ops[37] = {
     OP_STRING, /* STRING */
     -1,        /* GROUP */
     OP_SUBMSG, /* MESSAGE */
-    OP_STRING, /* BYTES */
+    OP_BYTES,  /* BYTES */
     -1,        /* UINT32 */
     -1,        /* ENUM */
     -1,        /* SFIXED32 */
@@ -123,7 +125,7 @@ static const int8_t delim_ops[37] = {
     OP_STRING,        /* REPEATED STRING */
     OP_SUBMSG,        /* REPEATED GROUP */
     OP_SUBMSG,        /* REPEATED MESSAGE */
-    OP_STRING,        /* REPEATED BYTES */
+    OP_BYTES,         /* REPEATED BYTES */
     OP_VARPCK_LG2(2), /* REPEATED UINT32 */
     OP_VARPCK_LG2(2), /* REPEATED ENUM */
     OP_FIXPCK_LG2(2), /* REPEATED SFIXED32 */
@@ -143,8 +145,6 @@ typedef struct {
 
 typedef union {
   bool bool_val;
-  int32_t int32_val;
-  int64_t int64_val;
   uint32_t uint32_val;
   uint64_t uint64_val;
   upb_strview str_val;
@@ -155,6 +155,40 @@ static const char *decode_msg(upb_decstate *d, const char *ptr, upb_msg *msg,
 
 UPB_NORETURN static void decode_err(upb_decstate *d) { longjmp(d->err, 1); }
 
+void decode_verifyutf8(upb_decstate *d, const char *buf, int len) {
+  static const uint8_t utf8_offset[] = {
+      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+      1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+      2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+      2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+      4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0,
+  };
+
+  int i, j;
+  uint8_t offset;
+
+  i = 0;
+  while (i < len) {
+    offset = utf8_offset[(uint8_t)buf[i]];
+    if (offset == 0 || i + offset > len) {
+      decode_err(d);
+    }
+    for (j = i + 1; j < i + offset; j++) {
+      if ((buf[j] & 0xc0) != 0x80) {
+        decode_err(d);
+      }
+    }
+    i += offset;
+  }
+  if (i != len) decode_err(d);
+}
+
 static bool decode_reserve(upb_decstate *d, upb_array *arr, size_t elem) {
   bool need_realloc = arr->size - arr->len < elem;
   if (need_realloc && !_upb_array_realloc(arr, arr->len + elem, d->arena)) {
@@ -209,14 +243,21 @@ static void decode_munge(int type, wireval *val) {
       break;
     case UPB_DESCRIPTOR_TYPE_SINT32: {
       uint32_t n = val->uint32_val;
-      val->int32_val = (n >> 1) ^ -(int32_t)(n & 1);
+      val->uint32_val = (n >> 1) ^ -(int32_t)(n & 1);
       break;
     }
     case UPB_DESCRIPTOR_TYPE_SINT64: {
       uint64_t n = val->uint64_val;
-      val->int64_val = (n >> 1) ^ -(int64_t)(n & 1);
+      val->uint64_val = (n >> 1) ^ -(int64_t)(n & 1);
       break;
     }
+    case UPB_DESCRIPTOR_TYPE_INT32:
+    case UPB_DESCRIPTOR_TYPE_UINT32:
+      if (!_upb_isle()) {
+        /* The next stage will memcpy(dst, &val, 4) */
+        val->uint32_val = val->uint64_val;
+      }
+      break;
   }
 }
 
@@ -300,7 +341,10 @@ static const char *decode_toarray(upb_decstate *d, const char *ptr,
       memcpy(mem, &val, 1 << op);
       return ptr;
     case OP_STRING:
-      /* Append string. */
+      decode_verifyutf8(d, val.str_val.data, val.str_val.size);
+      /* Fallthrough. */
+    case OP_BYTES:
+      /* Append bytes. */
       mem =
           UPB_PTR_AT(_upb_array_ptr(arr), arr->len * sizeof(upb_strview), void);
       arr->len++;
@@ -389,7 +433,7 @@ static void decode_tomap(upb_decstate *d, upb_msg *msg,
   if (entry->fields[1].descriptortype == UPB_DESCRIPTOR_TYPE_MESSAGE ||
       entry->fields[1].descriptortype == UPB_DESCRIPTOR_TYPE_GROUP) {
     /* Create proactively to handle the case where it doesn't appear. */
-    ent.v.val.val = (uint64_t)_upb_msg_new(entry->submsgs[0], d->arena);
+    ent.v.val = upb_value_ptr(_upb_msg_new(entry->submsgs[0], d->arena));
   }
 
   decode_tosubmsg(d, &ent.k, layout, field, val.str_val);
@@ -434,6 +478,9 @@ static const char *decode_tomsg(upb_decstate *d, const char *ptr, upb_msg *msg,
       break;
     }
     case OP_STRING:
+      decode_verifyutf8(d, val.str_val.data, val.str_val.size);
+      /* Fallthrough. */
+    case OP_BYTES:
       memcpy(mem, &val, sizeof(upb_strview));
       break;
     case OP_SCALAR_LG2(3):
@@ -477,14 +524,16 @@ static const char *decode_msg(upb_decstate *d, const char *ptr, upb_msg *msg,
         break;
       case UPB_WIRE_TYPE_32BIT:
         if (d->limit - ptr < 4) decode_err(d);
-        memcpy(&val, ptr, 4);
+        memcpy(&val.uint32_val, ptr, 4);
+        val.uint32_val = _upb_be_swap32(val.uint32_val);
         ptr += 4;
         op = OP_SCALAR_LG2(2);
         if (((1 << field->descriptortype) & fixed32_ok) == 0) goto unknown;
         break;
       case UPB_WIRE_TYPE_64BIT:
         if (d->limit - ptr < 8) decode_err(d);
-        memcpy(&val, ptr, 8);
+        memcpy(&val.uint64_val, ptr, 8);
+        val.uint64_val = _upb_be_swap64(val.uint64_val);
         ptr += 8;
         op = OP_SCALAR_LG2(3);
         if (((1 << field->descriptortype) & fixed64_ok) == 0) goto unknown;
@@ -504,7 +553,7 @@ static const char *decode_msg(upb_decstate *d, const char *ptr, upb_msg *msg,
         break;
       }
       case UPB_WIRE_TYPE_START_GROUP:
-        val.int32_val = field_number;
+        val.uint32_val = field_number;
         op = OP_SUBMSG;
         if (field->descriptortype != UPB_DTYPE_GROUP) goto unknown;
         break;
diff --git a/upb/def.c b/upb/def.c
index 8f143354001..00a5877bde2 100644
--- a/upb/def.c
+++ b/upb/def.c
@@ -922,6 +922,22 @@ static uint32_t upb_msglayout_place(upb_msglayout *l, size_t size) {
   return ret;
 }
 
+static int field_number_cmp(const void *p1, const void *p2) {
+  const upb_msglayout_field *f1 = p1;
+  const upb_msglayout_field *f2 = p2;
+  return f1->number - f2->number;
+}
+
+static void assign_layout_indices(const upb_msgdef *m, upb_msglayout_field *fields) {
+  int i;
+  int n = upb_msgdef_numfields(m);
+  for (i = 0; i < n; i++) {
+    upb_fielddef *f = (upb_fielddef*)upb_msgdef_itof(m, fields[i].number);
+    UPB_ASSERT(f);
+    f->layout_index = i;
+  }
+}
+
 /* This function is the dynamic equivalent of message_layout.{cc,h} in upbc.
  * It computes a dynamic layout for all of the fields in |m|. */
 static bool make_layout(const upb_symtab *symtab, const upb_msgdef *m) {
@@ -997,16 +1013,19 @@ static bool make_layout(const upb_symtab *symtab, const upb_msgdef *m) {
     field->descriptortype = upb_fielddef_descriptortype(f);
     field->label = upb_fielddef_label(f);
 
+    if (field->descriptortype == UPB_DTYPE_STRING &&
+        f->file->syntax == UPB_SYNTAX_PROTO2) {
+      /* See TableDescriptorType() in upbc/generator.cc for details and
+       * rationale. */
+      field->descriptortype = UPB_DTYPE_BYTES;
+    }
+
     if (upb_fielddef_ismap(f)) {
       field->label = _UPB_LABEL_MAP;
     } else if (upb_fielddef_packed(f)) {
       field->label = _UPB_LABEL_PACKED;
     }
 
-    /* TODO: we probably should sort the fields by field number to match the
-     * output of upbc, and to improve search speed for the table parser. */
-    f->layout_index = f->index_;
-
     if (upb_fielddef_issubmsg(f)) {
       const upb_msgdef *subm = upb_fielddef_msgsubdef(f);
       field->submsg_index = submsg_count++;
@@ -1079,6 +1098,10 @@ static bool make_layout(const upb_symtab *symtab, const upb_msgdef *m) {
    * alignment.  TODO: track overall alignment for real? */
   l->size = UPB_ALIGN_UP(l->size, 8);
 
+  /* Sort fields by number. */
+  qsort(fields, upb_msgdef_numfields(m), sizeof(*fields), field_number_cmp);
+  assign_layout_indices(m, fields);
+
   return true;
 }
 
@@ -1532,13 +1555,21 @@ static bool create_fielddef(
     f->oneof = NULL;
   }
 
-  if (google_protobuf_FieldDescriptorProto_has_options(field_proto)) {
-    options = google_protobuf_FieldDescriptorProto_options(field_proto);
-    f->lazy_ = google_protobuf_FieldOptions_lazy(options);
+  options = google_protobuf_FieldDescriptorProto_has_options(field_proto) ?
+    google_protobuf_FieldDescriptorProto_options(field_proto) : NULL;
+
+  if (options && google_protobuf_FieldOptions_has_packed(options)) {
     f->packed_ = google_protobuf_FieldOptions_packed(options);
+  } else {
+    /* Repeated fields default to packed for proto3 only. */
+    f->packed_ = upb_fielddef_isprimitive(f) &&
+        f->label_ == UPB_LABEL_REPEATED && f->file->syntax == UPB_SYNTAX_PROTO3;
+  }
+
+  if (options) {
+    f->lazy_ = google_protobuf_FieldOptions_lazy(options);
   } else {
     f->lazy_ = false;
-    f->packed_ = false;
   }
 
   return true;
diff --git a/upb/encode.c b/upb/encode.c
index 87162325ad9..a6ce62bfa53 100644
--- a/upb/encode.c
+++ b/upb/encode.c
@@ -77,12 +77,12 @@ static bool upb_put_bytes(upb_encstate *e, const void *data, size_t len) {
 }
 
 static bool upb_put_fixed64(upb_encstate *e, uint64_t val) {
-  /* TODO(haberman): byte-swap for big endian. */
+  val = _upb_be_swap64(val);
   return upb_put_bytes(e, &val, sizeof(uint64_t));
 }
 
 static bool upb_put_fixed32(upb_encstate *e, uint32_t val) {
-  /* TODO(haberman): byte-swap for big endian. */
+  val = _upb_be_swap32(val);
   return upb_put_bytes(e, &val, sizeof(uint32_t));
 }
 
diff --git a/upb/handlers.h b/upb/handlers.h
index 9ed70e959c6..53904b6022f 100644
--- a/upb/handlers.h
+++ b/upb/handlers.h
@@ -106,7 +106,7 @@ typedef struct {
 #define UPB_HANDLERATTR_INIT {NULL, NULL, NULL, false}
 
 /* Bufhandle, data passed along with a buffer to indicate its provenance. */
-typedef struct {
+struct upb_bufhandle {
   /* The beginning of the buffer.  This may be different than the pointer
    * passed to a StringBuf handler because the handler may receive data
    * that is from the middle or end of a larger buffer. */
@@ -133,7 +133,9 @@ typedef struct {
                                             : NULL;
   }
 #endif
-} upb_bufhandle;
+};
+
+typedef struct upb_bufhandle upb_bufhandle;
 
 #define UPB_BUFHANDLE_INIT {NULL, 0, NULL, NULL}
 
diff --git a/upb/json_decode.c b/upb/json_decode.c
index 9c70173165e..953d2387ed8 100644
--- a/upb/json_decode.c
+++ b/upb/json_decode.c
@@ -409,6 +409,7 @@ static upb_strview jsondec_string(jsondec *d) {
         upb_strview ret;
         ret.data = buf;
         ret.size = end - buf;
+        *end = '\0';  /* Needed for possible strtod(). */
         return ret;
       }
       case '\\':
diff --git a/upb/json_encode.c b/upb/json_encode.c
index 21f661f8f6e..6b6d99b30ff 100644
--- a/upb/json_encode.c
+++ b/upb/json_encode.c
@@ -38,6 +38,14 @@ UPB_NORETURN static void jsonenc_err(jsonenc *e, const char *msg) {
   longjmp(e->err, 1);
 }
 
+UPB_NORETURN static void jsonenc_errf(jsonenc *e, const char *fmt, ...) {
+  va_list argp;
+  va_start(argp, fmt);
+  upb_status_vseterrf(e->status, fmt, argp);
+  va_end(argp);
+  longjmp(e->err, 1);
+}
+
 static upb_arena *jsonenc_arena(jsonenc *e) {
   /* Create lazily, since it's only needed for Any */
   if (!e->arena) {
@@ -279,7 +287,11 @@ static const upb_msgdef *jsonenc_getanymsg(jsonenc *e, upb_strview type_url) {
   const char *ptr = end;
   const upb_msgdef *ret;
 
-  if (!e->ext_pool || type_url.size == 0) goto badurl;
+  if (!e->ext_pool) {
+    jsonenc_err(e, "Tried to encode Any, but no symtab was provided");
+  }
+
+  if (type_url.size == 0) goto badurl;
 
   while (true) {
     if (--ptr == type_url.data) {
@@ -295,13 +307,14 @@ static const upb_msgdef *jsonenc_getanymsg(jsonenc *e, upb_strview type_url) {
   ret = upb_symtab_lookupmsg2(e->ext_pool, ptr, end - ptr);
 
   if (!ret) {
-    jsonenc_err(e, "Couldn't find Any type");
+    jsonenc_errf(e, "Couldn't find Any type: %.*s", (int)(end - ptr), ptr);
   }
 
   return ret;
 
 badurl:
-  jsonenc_err(e, "Bad type URL");
+  jsonenc_errf(
+      e, "Bad type URL: " UPB_STRVIEW_FORMAT, UPB_STRVIEW_ARGS(type_url));
 }
 
 static void jsonenc_any(jsonenc *e, const upb_msg *msg, const upb_msgdef *m) {
diff --git a/upb/msg.h b/upb/msg.h
index b321748ec05..695c278b211 100644
--- a/upb/msg.h
+++ b/upb/msg.h
@@ -324,7 +324,7 @@ UPB_INLINE upb_value _upb_map_tovalue(const void *val, size_t size,
   if (size == UPB_MAPTYPE_STRING) {
     upb_strview *strp = (upb_strview*)upb_arena_malloc(a, sizeof(*strp));
     *strp = *(upb_strview*)val;
-    memcpy(&ret, &strp, sizeof(strp));
+    ret = upb_value_ptr(strp);
   } else {
     memcpy(&ret, val, size);
   }
@@ -455,7 +455,7 @@ UPB_INLINE void _upb_msg_map_set_value(void* msg, const void* val, size_t size)
   /* This is like _upb_map_tovalue() except the entry already exists so we can
    * reuse the allocated upb_strview for string fields. */
   if (size == UPB_MAPTYPE_STRING) {
-    upb_strview *strp = (upb_strview*)ent->val.val;
+    upb_strview *strp = (upb_strview*)(uintptr_t)ent->val.val;
     memcpy(strp, val, sizeof(*strp));
   } else {
     memcpy(&ent->val.val, val, size);
diff --git a/upb/port_def.inc b/upb/port_def.inc
index 9e9c506597f..2c144dc066a 100644
--- a/upb/port_def.inc
+++ b/upb/port_def.inc
@@ -141,7 +141,7 @@ int msvc_vsnprintf(char* s, size_t n, const char* format, va_list arg);
 #elif defined _MSC_VER
 #define UPB_ASSUME(expr) if (!(expr)) __assume(0)
 #else
-#define UPB_ASSUME(expr) do {} if (false && (expr))
+#define UPB_ASSUME(expr) do {} while (false && (expr))
 #endif
 #else
 #define UPB_ASSUME(expr) assert(expr)
diff --git a/upb/table.c b/upb/table.c
index e89f72c6988..34a20530d8b 100644
--- a/upb/table.c
+++ b/upb/table.c
@@ -559,17 +559,6 @@ bool upb_inttable_remove(upb_inttable *t, uintptr_t key, upb_value *val) {
   return success;
 }
 
-bool upb_inttable_push2(upb_inttable *t, upb_value val, upb_alloc *a) {
-  return upb_inttable_insert2(t, upb_inttable_count(t), val, a);
-}
-
-upb_value upb_inttable_pop(upb_inttable *t) {
-  upb_value val;
-  bool ok = upb_inttable_remove(t, upb_inttable_count(t) - 1, &val);
-  UPB_ASSERT(ok);
-  return val;
-}
-
 bool upb_inttable_insertptr2(upb_inttable *t, const void *key, upb_value val,
                              upb_alloc *a) {
   return upb_inttable_insert2(t, (uintptr_t)key, val, a);
diff --git a/upb/table.int.h b/upb/table.int.h
index 75575eb7d52..600637eef2e 100644
--- a/upb/table.int.h
+++ b/upb/table.int.h
@@ -326,15 +326,6 @@ UPB_INLINE bool upb_strtable_remove(upb_strtable *t, const char *key,
  * invalidate iterators. */
 bool upb_inttable_replace(upb_inttable *t, uintptr_t key, upb_value val);
 
-/* Handy routines for treating an inttable like a stack.  May not be mixed with
- * other insert/remove calls. */
-bool upb_inttable_push2(upb_inttable *t, upb_value val, upb_alloc *a);
-upb_value upb_inttable_pop(upb_inttable *t);
-
-UPB_INLINE bool upb_inttable_push(upb_inttable *t, upb_value val) {
-  return upb_inttable_push2(t, val, &upb_alloc_global);
-}
-
 /* Convenience routines for inttables with pointer keys. */
 bool upb_inttable_insertptr2(upb_inttable *t, const void *key, upb_value val,
                              upb_alloc *a);
diff --git a/upb/upb.c b/upb/upb.c
index c2d406950d3..3089c059e1e 100644
--- a/upb/upb.c
+++ b/upb/upb.c
@@ -106,15 +106,28 @@ struct upb_arena {
 
 static const size_t memblock_reserve = UPB_ALIGN_UP(sizeof(mem_block), 16);
 
+static upb_arena *arena_findroot(upb_arena *a) {
+  /* Path splitting keeps time complexity down, see:
+   *   https://en.wikipedia.org/wiki/Disjoint-set_data_structure */
+  while (a->parent != a) {
+    upb_arena *next = a->parent;
+    a->parent = next->parent;
+    a = next;
+  }
+  return a;
+}
+
 static void upb_arena_addblock(upb_arena *a, void *ptr, size_t size) {
   mem_block *block = ptr;
+  upb_arena *root = arena_findroot(a);
 
-  block->next = a->freelist;
+  /* The block is for arena |a|, but should appear in the freelist of |root|. */
+  block->next = root->freelist;
   block->size = (uint32_t)size;
   block->cleanups = 0;
-  a->freelist = block;
+  root->freelist = block;
   a->last_size = block->size;
-  if (!a->freelist_tail) a->freelist_tail = block;
+  if (!root->freelist_tail) root->freelist_tail = block;
 
   a->head.ptr = UPB_PTR_AT(block, memblock_reserve, char);
   a->head.end = UPB_PTR_AT(block, size, char);
@@ -149,17 +162,6 @@ static void *upb_arena_doalloc(upb_alloc *alloc, void *ptr, size_t oldsize,
   return upb_arena_realloc(a, ptr, oldsize, size);
 }
 
-static upb_arena *arena_findroot(upb_arena *a) {
-  /* Path splitting keeps time complexity down, see:
-   *   https://en.wikipedia.org/wiki/Disjoint-set_data_structure */
-  while (a->parent != a) {
-    upb_arena *next = a->parent;
-    a->parent = next->parent;
-    a = next;
-  }
-  return a;
-}
-
 /* Public Arena API ***********************************************************/
 
 upb_arena *arena_initslow(void *mem, size_t n, upb_alloc *alloc) {
diff --git a/upb/upb.h b/upb/upb.h
index c3e1c5f3d59..e1d9d8cfd35 100644
--- a/upb/upb.h
+++ b/upb/upb.h
@@ -273,6 +273,32 @@ typedef enum {
 
 #define UPB_MAP_BEGIN ((size_t)-1)
 
+UPB_INLINE bool _upb_isle(void) {
+  int x = 1;
+  return *(char*)&x == 1;
+}
+
+UPB_INLINE uint32_t _upb_be_swap32(uint32_t val) {
+  if (_upb_isle()) {
+    return val;
+  } else {
+    return ((val & 0xff) << 24) | ((val & 0xff00) << 8) |
+           ((val & 0xff0000ULL) >> 8) | ((val & 0xff000000ULL) >> 24);
+  }
+}
+
+UPB_INLINE uint64_t _upb_be_swap64(uint64_t val) {
+  if (_upb_isle()) {
+    return val;
+  } else {
+    return ((val & 0xff) << 56) | ((val & 0xff00) << 40) |
+           ((val & 0xff0000) << 24) | ((val & 0xff000000) << 8) |
+           ((val & 0xff00000000ULL) >> 8) | ((val & 0xff0000000000ULL) >> 24) |
+           ((val & 0xff000000000000ULL) >> 40) |
+           ((val & 0xff00000000000000ULL) >> 56);
+  }
+}
+
 #include "upb/port_undef.inc"
 
 #ifdef __cplusplus
diff --git a/upb/upb.hpp b/upb/upb.hpp
index 60ef29b2966..a3ec5faff8f 100644
--- a/upb/upb.hpp
+++ b/upb/upb.hpp
@@ -60,6 +60,8 @@ class Arena {
     });
   }
 
+  void Fuse(Arena& other) { upb_arena_fuse(ptr(), other.ptr()); }
+
  private:
   std::unique_ptr<upb_arena, decltype(&upb_arena_free)> ptr_;
 };
diff --git a/upbc/generator.cc b/upbc/generator.cc
index d5c27fb2afa..72df0244aa1 100644
--- a/upbc/generator.cc
+++ b/upbc/generator.cc
@@ -281,7 +281,9 @@ std::string FieldDefault(const protobuf::FieldDescriptor* field) {
     case protobuf::FieldDescriptor::CPPTYPE_BOOL:
       return field->default_value_bool() ? "true" : "false";
     case protobuf::FieldDescriptor::CPPTYPE_ENUM:
-      return EnumValueSymbol(field->default_value_enum());
+      // Use a number instead of a symbolic name so that we don't require
+      // this enum's header to be included.
+      return absl::StrCat(field->default_value_enum()->number());
   }
   ABSL_ASSERT(false);
   return "XXX";
@@ -685,6 +687,22 @@ void WriteHeader(const protobuf::FileDescriptor* file, Output& output) {
       ToPreproc(file->name()));
 }
 
+int TableDescriptorType(const protobuf::FieldDescriptor* field) {
+  if (field->file()->syntax() == protobuf::FileDescriptor::SYNTAX_PROTO2 &&
+      field->type() == protobuf::FieldDescriptor::TYPE_STRING) {
+    // From the perspective of the binary encoder/decoder, proto2 string fields
+    // are identical to bytes fields. Only in proto3 do we check UTF-8 for
+    // string fields at parse time.
+    //
+    // If we ever use these tables for JSON encoding/decoding (for example by
+    // embedding field names on the side) we will have to revisit this, because
+    // string vs. bytes behavior is not affected by proto2 vs proto3.
+    return protobuf::FieldDescriptor::TYPE_BYTES;
+  } else {
+    return field->type();
+  }
+}
+
 void WriteSource(const protobuf::FileDescriptor* file, Output& output) {
   EmitFileWarning(file, output);
 
@@ -779,7 +797,7 @@ void WriteSource(const protobuf::FileDescriptor* file, Output& output) {
                GetSizeInit(layout.GetFieldOffset(field)),
                presence,
                submsg_index,
-               field->type(),
+               TableDescriptorType(field),
                label);
       }
       output("};\n\n");

From e2e6b1839e83b7373edda903fb17dd15a2cb996f Mon Sep 17 00:00:00 2001
From: Esun Kim <veblush@google.com>
Date: Mon, 3 Aug 2020 14:28:48 -0700
Subject: [PATCH 36/38] Update upb in bazel

---
 bazel/grpc_deps.bzl | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/bazel/grpc_deps.bzl b/bazel/grpc_deps.bzl
index e53777d0743..335c1f02e2a 100644
--- a/bazel/grpc_deps.bzl
+++ b/bazel/grpc_deps.bzl
@@ -283,11 +283,11 @@ def grpc_deps():
     if "upb" not in native.existing_rules():
         http_archive(
             name = "upb",
-            sha256 = "79f7de61203c4ee5e4fcb2f17c5f3338119d6eb94aca8bce05332d2c1cfee108",
-            strip_prefix = "upb-92e63da73328d01b417cf26c2de7b0a27a0f83af",
+            sha256 = "7992217989f3156f8109931c1fc6db3434b7414957cb82371552377beaeb9d6c",
+            strip_prefix = "upb-382d5afc60e05470c23e8de19b19fc5ad231e732",
             urls = [
-                "https://storage.googleapis.com/grpc-bazel-mirror/github.com/protocolbuffers/upb/archive/92e63da73328d01b417cf26c2de7b0a27a0f83af.tar.gz",
-                "https://github.com/protocolbuffers/upb/archive/92e63da73328d01b417cf26c2de7b0a27a0f83af.tar.gz",
+                "https://storage.googleapis.com/grpc-bazel-mirror/github.com/protocolbuffers/upb/archive/382d5afc60e05470c23e8de19b19fc5ad231e732.tar.gz",
+                "https://github.com/protocolbuffers/upb/archive/382d5afc60e05470c23e8de19b19fc5ad231e732.tar.gz",
             ],
         )
 

From a7ffb7e47c88aaea6bbbea697be514b8ed55b921 Mon Sep 17 00:00:00 2001
From: Esun Kim <veblush@google.com>
Date: Mon, 3 Aug 2020 14:38:30 -0700
Subject: [PATCH 37/38] Regen upb files

---
 .../envoy/config/cluster/v3/cluster.upb.h     |  2 +-
 .../google/api/expr/v1alpha1/syntax.upb.h     |  2 +-
 .../google/protobuf/descriptor.upb.c          | 72 +++++++++----------
 .../google/protobuf/struct.upb.h              |  2 +-
 .../ext/upb-generated/validate/validate.upb.c | 22 +++---
 .../ext/upb-generated/validate/validate.upb.h |  2 +-
 6 files changed, 51 insertions(+), 51 deletions(-)

diff --git a/src/core/ext/upb-generated/envoy/config/cluster/v3/cluster.upb.h b/src/core/ext/upb-generated/envoy/config/cluster/v3/cluster.upb.h
index f415862e696..06270bd96bc 100644
--- a/src/core/ext/upb-generated/envoy/config/cluster/v3/cluster.upb.h
+++ b/src/core/ext/upb-generated/envoy/config/cluster/v3/cluster.upb.h
@@ -199,7 +199,7 @@ UPB_INLINE envoy_config_cluster_v3_Cluster_lb_config_oneofcases envoy_config_clu
 
 UPB_INLINE upb_strview envoy_config_cluster_v3_Cluster_name(const envoy_config_cluster_v3_Cluster *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(32, 32), upb_strview); }
 UPB_INLINE bool envoy_config_cluster_v3_Cluster_has_type(const envoy_config_cluster_v3_Cluster *msg) { return _upb_getoneofcase(msg, UPB_SIZE(168, 288)) == 2; }
-UPB_INLINE int32_t envoy_config_cluster_v3_Cluster_type(const envoy_config_cluster_v3_Cluster *msg) { return UPB_READ_ONEOF(msg, int32_t, UPB_SIZE(160, 280), UPB_SIZE(168, 288), 2, envoy_config_cluster_v3_Cluster_STATIC); }
+UPB_INLINE int32_t envoy_config_cluster_v3_Cluster_type(const envoy_config_cluster_v3_Cluster *msg) { return UPB_READ_ONEOF(msg, int32_t, UPB_SIZE(160, 280), UPB_SIZE(168, 288), 2, 0); }
 UPB_INLINE bool envoy_config_cluster_v3_Cluster_has_eds_cluster_config(const envoy_config_cluster_v3_Cluster *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(48, 64)); }
 UPB_INLINE const envoy_config_cluster_v3_Cluster_EdsClusterConfig* envoy_config_cluster_v3_Cluster_eds_cluster_config(const envoy_config_cluster_v3_Cluster *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(48, 64), const envoy_config_cluster_v3_Cluster_EdsClusterConfig*); }
 UPB_INLINE bool envoy_config_cluster_v3_Cluster_has_connect_timeout(const envoy_config_cluster_v3_Cluster *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(52, 72)); }
diff --git a/src/core/ext/upb-generated/google/api/expr/v1alpha1/syntax.upb.h b/src/core/ext/upb-generated/google/api/expr/v1alpha1/syntax.upb.h
index a3cab46af1a..6c4b7f81eb1 100644
--- a/src/core/ext/upb-generated/google/api/expr/v1alpha1/syntax.upb.h
+++ b/src/core/ext/upb-generated/google/api/expr/v1alpha1/syntax.upb.h
@@ -594,7 +594,7 @@ typedef enum {
 UPB_INLINE google_api_expr_v1alpha1_Constant_constant_kind_oneofcases google_api_expr_v1alpha1_Constant_constant_kind_case(const google_api_expr_v1alpha1_Constant* msg) { return (google_api_expr_v1alpha1_Constant_constant_kind_oneofcases)*UPB_PTR_AT(msg, UPB_SIZE(8, 16), int32_t); }
 
 UPB_INLINE bool google_api_expr_v1alpha1_Constant_has_null_value(const google_api_expr_v1alpha1_Constant *msg) { return _upb_getoneofcase(msg, UPB_SIZE(8, 16)) == 1; }
-UPB_INLINE int32_t google_api_expr_v1alpha1_Constant_null_value(const google_api_expr_v1alpha1_Constant *msg) { return UPB_READ_ONEOF(msg, int32_t, UPB_SIZE(0, 0), UPB_SIZE(8, 16), 1, google_protobuf_NULL_VALUE); }
+UPB_INLINE int32_t google_api_expr_v1alpha1_Constant_null_value(const google_api_expr_v1alpha1_Constant *msg) { return UPB_READ_ONEOF(msg, int32_t, UPB_SIZE(0, 0), UPB_SIZE(8, 16), 1, 0); }
 UPB_INLINE bool google_api_expr_v1alpha1_Constant_has_bool_value(const google_api_expr_v1alpha1_Constant *msg) { return _upb_getoneofcase(msg, UPB_SIZE(8, 16)) == 2; }
 UPB_INLINE bool google_api_expr_v1alpha1_Constant_bool_value(const google_api_expr_v1alpha1_Constant *msg) { return UPB_READ_ONEOF(msg, bool, UPB_SIZE(0, 0), UPB_SIZE(8, 16), 2, false); }
 UPB_INLINE bool google_api_expr_v1alpha1_Constant_has_int64_value(const google_api_expr_v1alpha1_Constant *msg) { return _upb_getoneofcase(msg, UPB_SIZE(8, 16)) == 3; }
diff --git a/src/core/ext/upb-generated/google/protobuf/descriptor.upb.c b/src/core/ext/upb-generated/google/protobuf/descriptor.upb.c
index 3c7363922e9..44cd3ae2cec 100644
--- a/src/core/ext/upb-generated/google/protobuf/descriptor.upb.c
+++ b/src/core/ext/upb-generated/google/protobuf/descriptor.upb.c
@@ -36,9 +36,9 @@ static const upb_msglayout *const google_protobuf_FileDescriptorProto_submsgs[6]
 };
 
 static const upb_msglayout_field google_protobuf_FileDescriptorProto__fields[12] = {
-  {1, UPB_SIZE(4, 8), 1, 0, 9, 1},
-  {2, UPB_SIZE(12, 24), 2, 0, 9, 1},
-  {3, UPB_SIZE(36, 72), 0, 0, 9, 3},
+  {1, UPB_SIZE(4, 8), 1, 0, 12, 1},
+  {2, UPB_SIZE(12, 24), 2, 0, 12, 1},
+  {3, UPB_SIZE(36, 72), 0, 0, 12, 3},
   {4, UPB_SIZE(40, 80), 0, 0, 11, 3},
   {5, UPB_SIZE(44, 88), 0, 1, 11, 3},
   {6, UPB_SIZE(48, 96), 0, 4, 11, 3},
@@ -47,7 +47,7 @@ static const upb_msglayout_field google_protobuf_FileDescriptorProto__fields[12]
   {9, UPB_SIZE(32, 64), 5, 5, 11, 1},
   {10, UPB_SIZE(56, 112), 0, 0, 5, 3},
   {11, UPB_SIZE(60, 120), 0, 0, 5, 3},
-  {12, UPB_SIZE(20, 40), 3, 0, 9, 1},
+  {12, UPB_SIZE(20, 40), 3, 0, 12, 1},
 };
 
 const upb_msglayout google_protobuf_FileDescriptorProto_msginit = {
@@ -67,7 +67,7 @@ static const upb_msglayout *const google_protobuf_DescriptorProto_submsgs[8] = {
 };
 
 static const upb_msglayout_field google_protobuf_DescriptorProto__fields[10] = {
-  {1, UPB_SIZE(4, 8), 1, 0, 9, 1},
+  {1, UPB_SIZE(4, 8), 1, 0, 12, 1},
   {2, UPB_SIZE(16, 32), 0, 4, 11, 3},
   {3, UPB_SIZE(20, 40), 0, 0, 11, 3},
   {4, UPB_SIZE(24, 48), 0, 3, 11, 3},
@@ -76,7 +76,7 @@ static const upb_msglayout_field google_protobuf_DescriptorProto__fields[10] = {
   {7, UPB_SIZE(12, 24), 2, 5, 11, 1},
   {8, UPB_SIZE(36, 72), 0, 6, 11, 3},
   {9, UPB_SIZE(40, 80), 0, 2, 11, 3},
-  {10, UPB_SIZE(44, 88), 0, 0, 9, 3},
+  {10, UPB_SIZE(44, 88), 0, 0, 12, 3},
 };
 
 const upb_msglayout google_protobuf_DescriptorProto_msginit = {
@@ -131,16 +131,16 @@ static const upb_msglayout *const google_protobuf_FieldDescriptorProto_submsgs[1
 };
 
 static const upb_msglayout_field google_protobuf_FieldDescriptorProto__fields[11] = {
-  {1, UPB_SIZE(36, 40), 6, 0, 9, 1},
-  {2, UPB_SIZE(44, 56), 7, 0, 9, 1},
+  {1, UPB_SIZE(36, 40), 6, 0, 12, 1},
+  {2, UPB_SIZE(44, 56), 7, 0, 12, 1},
   {3, UPB_SIZE(24, 24), 3, 0, 5, 1},
   {4, UPB_SIZE(8, 8), 1, 0, 14, 1},
   {5, UPB_SIZE(16, 16), 2, 0, 14, 1},
-  {6, UPB_SIZE(52, 72), 8, 0, 9, 1},
-  {7, UPB_SIZE(60, 88), 9, 0, 9, 1},
+  {6, UPB_SIZE(52, 72), 8, 0, 12, 1},
+  {7, UPB_SIZE(60, 88), 9, 0, 12, 1},
   {8, UPB_SIZE(76, 120), 11, 0, 11, 1},
   {9, UPB_SIZE(28, 28), 4, 0, 5, 1},
-  {10, UPB_SIZE(68, 104), 10, 0, 9, 1},
+  {10, UPB_SIZE(68, 104), 10, 0, 12, 1},
   {17, UPB_SIZE(32, 32), 5, 0, 8, 1},
 };
 
@@ -155,7 +155,7 @@ static const upb_msglayout *const google_protobuf_OneofDescriptorProto_submsgs[1
 };
 
 static const upb_msglayout_field google_protobuf_OneofDescriptorProto__fields[2] = {
-  {1, UPB_SIZE(4, 8), 1, 0, 9, 1},
+  {1, UPB_SIZE(4, 8), 1, 0, 12, 1},
   {2, UPB_SIZE(12, 24), 2, 0, 11, 1},
 };
 
@@ -172,11 +172,11 @@ static const upb_msglayout *const google_protobuf_EnumDescriptorProto_submsgs[3]
 };
 
 static const upb_msglayout_field google_protobuf_EnumDescriptorProto__fields[5] = {
-  {1, UPB_SIZE(4, 8), 1, 0, 9, 1},
+  {1, UPB_SIZE(4, 8), 1, 0, 12, 1},
   {2, UPB_SIZE(16, 32), 0, 2, 11, 3},
   {3, UPB_SIZE(12, 24), 2, 1, 11, 1},
   {4, UPB_SIZE(20, 40), 0, 0, 11, 3},
-  {5, UPB_SIZE(24, 48), 0, 0, 9, 3},
+  {5, UPB_SIZE(24, 48), 0, 0, 12, 3},
 };
 
 const upb_msglayout google_protobuf_EnumDescriptorProto_msginit = {
@@ -201,7 +201,7 @@ static const upb_msglayout *const google_protobuf_EnumValueDescriptorProto_subms
 };
 
 static const upb_msglayout_field google_protobuf_EnumValueDescriptorProto__fields[3] = {
-  {1, UPB_SIZE(8, 8), 2, 0, 9, 1},
+  {1, UPB_SIZE(8, 8), 2, 0, 12, 1},
   {2, UPB_SIZE(4, 4), 1, 0, 5, 1},
   {3, UPB_SIZE(16, 24), 3, 0, 11, 1},
 };
@@ -218,7 +218,7 @@ static const upb_msglayout *const google_protobuf_ServiceDescriptorProto_submsgs
 };
 
 static const upb_msglayout_field google_protobuf_ServiceDescriptorProto__fields[3] = {
-  {1, UPB_SIZE(4, 8), 1, 0, 9, 1},
+  {1, UPB_SIZE(4, 8), 1, 0, 12, 1},
   {2, UPB_SIZE(16, 32), 0, 0, 11, 3},
   {3, UPB_SIZE(12, 24), 2, 1, 11, 1},
 };
@@ -234,9 +234,9 @@ static const upb_msglayout *const google_protobuf_MethodDescriptorProto_submsgs[
 };
 
 static const upb_msglayout_field google_protobuf_MethodDescriptorProto__fields[6] = {
-  {1, UPB_SIZE(4, 8), 3, 0, 9, 1},
-  {2, UPB_SIZE(12, 24), 4, 0, 9, 1},
-  {3, UPB_SIZE(20, 40), 5, 0, 9, 1},
+  {1, UPB_SIZE(4, 8), 3, 0, 12, 1},
+  {2, UPB_SIZE(12, 24), 4, 0, 12, 1},
+  {3, UPB_SIZE(20, 40), 5, 0, 12, 1},
   {4, UPB_SIZE(28, 56), 6, 0, 11, 1},
   {5, UPB_SIZE(1, 1), 1, 0, 8, 1},
   {6, UPB_SIZE(2, 2), 2, 0, 8, 1},
@@ -253,11 +253,11 @@ static const upb_msglayout *const google_protobuf_FileOptions_submsgs[1] = {
 };
 
 static const upb_msglayout_field google_protobuf_FileOptions__fields[21] = {
-  {1, UPB_SIZE(28, 32), 11, 0, 9, 1},
-  {8, UPB_SIZE(36, 48), 12, 0, 9, 1},
+  {1, UPB_SIZE(28, 32), 11, 0, 12, 1},
+  {8, UPB_SIZE(36, 48), 12, 0, 12, 1},
   {9, UPB_SIZE(8, 8), 1, 0, 14, 1},
   {10, UPB_SIZE(16, 16), 2, 0, 8, 1},
-  {11, UPB_SIZE(44, 64), 13, 0, 9, 1},
+  {11, UPB_SIZE(44, 64), 13, 0, 12, 1},
   {16, UPB_SIZE(17, 17), 3, 0, 8, 1},
   {17, UPB_SIZE(18, 18), 4, 0, 8, 1},
   {18, UPB_SIZE(19, 19), 5, 0, 8, 1},
@@ -265,14 +265,14 @@ static const upb_msglayout_field google_protobuf_FileOptions__fields[21] = {
   {23, UPB_SIZE(21, 21), 7, 0, 8, 1},
   {27, UPB_SIZE(22, 22), 8, 0, 8, 1},
   {31, UPB_SIZE(23, 23), 9, 0, 8, 1},
-  {36, UPB_SIZE(52, 80), 14, 0, 9, 1},
-  {37, UPB_SIZE(60, 96), 15, 0, 9, 1},
-  {39, UPB_SIZE(68, 112), 16, 0, 9, 1},
-  {40, UPB_SIZE(76, 128), 17, 0, 9, 1},
-  {41, UPB_SIZE(84, 144), 18, 0, 9, 1},
+  {36, UPB_SIZE(52, 80), 14, 0, 12, 1},
+  {37, UPB_SIZE(60, 96), 15, 0, 12, 1},
+  {39, UPB_SIZE(68, 112), 16, 0, 12, 1},
+  {40, UPB_SIZE(76, 128), 17, 0, 12, 1},
+  {41, UPB_SIZE(84, 144), 18, 0, 12, 1},
   {42, UPB_SIZE(24, 24), 10, 0, 8, 1},
-  {44, UPB_SIZE(92, 160), 19, 0, 9, 1},
-  {45, UPB_SIZE(100, 176), 20, 0, 9, 1},
+  {44, UPB_SIZE(92, 160), 19, 0, 12, 1},
+  {45, UPB_SIZE(100, 176), 20, 0, 12, 1},
   {999, UPB_SIZE(108, 192), 0, 0, 11, 3},
 };
 
@@ -402,12 +402,12 @@ static const upb_msglayout *const google_protobuf_UninterpretedOption_submsgs[1]
 
 static const upb_msglayout_field google_protobuf_UninterpretedOption__fields[7] = {
   {2, UPB_SIZE(56, 80), 0, 0, 11, 3},
-  {3, UPB_SIZE(32, 32), 4, 0, 9, 1},
+  {3, UPB_SIZE(32, 32), 4, 0, 12, 1},
   {4, UPB_SIZE(8, 8), 1, 0, 4, 1},
   {5, UPB_SIZE(16, 16), 2, 0, 3, 1},
   {6, UPB_SIZE(24, 24), 3, 0, 1, 1},
   {7, UPB_SIZE(40, 48), 5, 0, 12, 1},
-  {8, UPB_SIZE(48, 64), 6, 0, 9, 1},
+  {8, UPB_SIZE(48, 64), 6, 0, 12, 1},
 };
 
 const upb_msglayout google_protobuf_UninterpretedOption_msginit = {
@@ -417,7 +417,7 @@ const upb_msglayout google_protobuf_UninterpretedOption_msginit = {
 };
 
 static const upb_msglayout_field google_protobuf_UninterpretedOption_NamePart__fields[2] = {
-  {1, UPB_SIZE(4, 8), 2, 0, 9, 2},
+  {1, UPB_SIZE(4, 8), 2, 0, 12, 2},
   {2, UPB_SIZE(1, 1), 1, 0, 8, 2},
 };
 
@@ -444,9 +444,9 @@ const upb_msglayout google_protobuf_SourceCodeInfo_msginit = {
 static const upb_msglayout_field google_protobuf_SourceCodeInfo_Location__fields[5] = {
   {1, UPB_SIZE(20, 40), 0, 0, 5, _UPB_LABEL_PACKED},
   {2, UPB_SIZE(24, 48), 0, 0, 5, _UPB_LABEL_PACKED},
-  {3, UPB_SIZE(4, 8), 1, 0, 9, 1},
-  {4, UPB_SIZE(12, 24), 2, 0, 9, 1},
-  {6, UPB_SIZE(28, 56), 0, 0, 9, 3},
+  {3, UPB_SIZE(4, 8), 1, 0, 12, 1},
+  {4, UPB_SIZE(12, 24), 2, 0, 12, 1},
+  {6, UPB_SIZE(28, 56), 0, 0, 12, 3},
 };
 
 const upb_msglayout google_protobuf_SourceCodeInfo_Location_msginit = {
@@ -471,7 +471,7 @@ const upb_msglayout google_protobuf_GeneratedCodeInfo_msginit = {
 
 static const upb_msglayout_field google_protobuf_GeneratedCodeInfo_Annotation__fields[4] = {
   {1, UPB_SIZE(20, 32), 0, 0, 5, _UPB_LABEL_PACKED},
-  {2, UPB_SIZE(12, 16), 3, 0, 9, 1},
+  {2, UPB_SIZE(12, 16), 3, 0, 12, 1},
   {3, UPB_SIZE(4, 4), 1, 0, 5, 1},
   {4, UPB_SIZE(8, 8), 2, 0, 5, 1},
 };
diff --git a/src/core/ext/upb-generated/google/protobuf/struct.upb.h b/src/core/ext/upb-generated/google/protobuf/struct.upb.h
index 962431ecf48..b25201d3234 100644
--- a/src/core/ext/upb-generated/google/protobuf/struct.upb.h
+++ b/src/core/ext/upb-generated/google/protobuf/struct.upb.h
@@ -105,7 +105,7 @@ typedef enum {
 UPB_INLINE google_protobuf_Value_kind_oneofcases google_protobuf_Value_kind_case(const google_protobuf_Value* msg) { return (google_protobuf_Value_kind_oneofcases)*UPB_PTR_AT(msg, UPB_SIZE(8, 16), int32_t); }
 
 UPB_INLINE bool google_protobuf_Value_has_null_value(const google_protobuf_Value *msg) { return _upb_getoneofcase(msg, UPB_SIZE(8, 16)) == 1; }
-UPB_INLINE int32_t google_protobuf_Value_null_value(const google_protobuf_Value *msg) { return UPB_READ_ONEOF(msg, int32_t, UPB_SIZE(0, 0), UPB_SIZE(8, 16), 1, google_protobuf_NULL_VALUE); }
+UPB_INLINE int32_t google_protobuf_Value_null_value(const google_protobuf_Value *msg) { return UPB_READ_ONEOF(msg, int32_t, UPB_SIZE(0, 0), UPB_SIZE(8, 16), 1, 0); }
 UPB_INLINE bool google_protobuf_Value_has_number_value(const google_protobuf_Value *msg) { return _upb_getoneofcase(msg, UPB_SIZE(8, 16)) == 2; }
 UPB_INLINE double google_protobuf_Value_number_value(const google_protobuf_Value *msg) { return UPB_READ_ONEOF(msg, double, UPB_SIZE(0, 0), UPB_SIZE(8, 16), 2, 0); }
 UPB_INLINE bool google_protobuf_Value_has_string_value(const google_protobuf_Value *msg) { return _upb_getoneofcase(msg, UPB_SIZE(8, 16)) == 3; }
diff --git a/src/core/ext/upb-generated/validate/validate.upb.c b/src/core/ext/upb-generated/validate/validate.upb.c
index 1d6ab9bca75..63c324368d7 100644
--- a/src/core/ext/upb-generated/validate/validate.upb.c
+++ b/src/core/ext/upb-generated/validate/validate.upb.c
@@ -274,17 +274,17 @@ const upb_msglayout validate_BoolRules_msginit = {
 };
 
 static const upb_msglayout_field validate_StringRules__fields[25] = {
-  {1, UPB_SIZE(60, 64), 8, 0, 9, 1},
+  {1, UPB_SIZE(60, 64), 8, 0, 12, 1},
   {2, UPB_SIZE(8, 8), 1, 0, 4, 1},
   {3, UPB_SIZE(16, 16), 2, 0, 4, 1},
   {4, UPB_SIZE(24, 24), 3, 0, 4, 1},
   {5, UPB_SIZE(32, 32), 4, 0, 4, 1},
-  {6, UPB_SIZE(68, 80), 9, 0, 9, 1},
-  {7, UPB_SIZE(76, 96), 10, 0, 9, 1},
-  {8, UPB_SIZE(84, 112), 11, 0, 9, 1},
-  {9, UPB_SIZE(92, 128), 12, 0, 9, 1},
-  {10, UPB_SIZE(108, 160), 0, 0, 9, 3},
-  {11, UPB_SIZE(112, 168), 0, 0, 9, 3},
+  {6, UPB_SIZE(68, 80), 9, 0, 12, 1},
+  {7, UPB_SIZE(76, 96), 10, 0, 12, 1},
+  {8, UPB_SIZE(84, 112), 11, 0, 12, 1},
+  {9, UPB_SIZE(92, 128), 12, 0, 12, 1},
+  {10, UPB_SIZE(108, 160), 0, 0, 12, 3},
+  {11, UPB_SIZE(112, 168), 0, 0, 12, 3},
   {12, UPB_SIZE(120, 176), UPB_SIZE(-129, -185), 0, 8, 1},
   {13, UPB_SIZE(120, 176), UPB_SIZE(-129, -185), 0, 8, 1},
   {14, UPB_SIZE(120, 176), UPB_SIZE(-129, -185), 0, 8, 1},
@@ -296,7 +296,7 @@ static const upb_msglayout_field validate_StringRules__fields[25] = {
   {20, UPB_SIZE(48, 48), 6, 0, 4, 1},
   {21, UPB_SIZE(120, 176), UPB_SIZE(-129, -185), 0, 8, 1},
   {22, UPB_SIZE(120, 176), UPB_SIZE(-129, -185), 0, 8, 1},
-  {23, UPB_SIZE(100, 144), 13, 0, 9, 1},
+  {23, UPB_SIZE(100, 144), 13, 0, 12, 1},
   {24, UPB_SIZE(120, 176), UPB_SIZE(-129, -185), 0, 14, 1},
   {25, UPB_SIZE(56, 56), 7, 0, 8, 1},
 };
@@ -311,7 +311,7 @@ static const upb_msglayout_field validate_BytesRules__fields[13] = {
   {1, UPB_SIZE(32, 32), 4, 0, 12, 1},
   {2, UPB_SIZE(8, 8), 1, 0, 4, 1},
   {3, UPB_SIZE(16, 16), 2, 0, 4, 1},
-  {4, UPB_SIZE(40, 48), 5, 0, 9, 1},
+  {4, UPB_SIZE(40, 48), 5, 0, 12, 1},
   {5, UPB_SIZE(48, 64), 6, 0, 12, 1},
   {6, UPB_SIZE(56, 80), 7, 0, 12, 1},
   {7, UPB_SIZE(64, 96), 8, 0, 12, 1},
@@ -390,8 +390,8 @@ const upb_msglayout validate_MapRules_msginit = {
 
 static const upb_msglayout_field validate_AnyRules__fields[3] = {
   {1, UPB_SIZE(1, 1), 1, 0, 8, 1},
-  {2, UPB_SIZE(4, 8), 0, 0, 9, 3},
-  {3, UPB_SIZE(8, 16), 0, 0, 9, 3},
+  {2, UPB_SIZE(4, 8), 0, 0, 12, 3},
+  {3, UPB_SIZE(8, 16), 0, 0, 12, 3},
 };
 
 const upb_msglayout validate_AnyRules_msginit = {
diff --git a/src/core/ext/upb-generated/validate/validate.upb.h b/src/core/ext/upb-generated/validate/validate.upb.h
index 7fde5b75a4b..c29b4ec589b 100644
--- a/src/core/ext/upb-generated/validate/validate.upb.h
+++ b/src/core/ext/upb-generated/validate/validate.upb.h
@@ -1363,7 +1363,7 @@ UPB_INLINE bool validate_StringRules_uuid(const validate_StringRules *msg) { ret
 UPB_INLINE bool validate_StringRules_has_not_contains(const validate_StringRules *msg) { return _upb_hasbit(msg, 13); }
 UPB_INLINE upb_strview validate_StringRules_not_contains(const validate_StringRules *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(100, 144), upb_strview); }
 UPB_INLINE bool validate_StringRules_has_well_known_regex(const validate_StringRules *msg) { return _upb_getoneofcase(msg, UPB_SIZE(128, 184)) == 24; }
-UPB_INLINE int32_t validate_StringRules_well_known_regex(const validate_StringRules *msg) { return UPB_READ_ONEOF(msg, int32_t, UPB_SIZE(120, 176), UPB_SIZE(128, 184), 24, validate_UNKNOWN); }
+UPB_INLINE int32_t validate_StringRules_well_known_regex(const validate_StringRules *msg) { return UPB_READ_ONEOF(msg, int32_t, UPB_SIZE(120, 176), UPB_SIZE(128, 184), 24, 0); }
 UPB_INLINE bool validate_StringRules_has_strict(const validate_StringRules *msg) { return _upb_hasbit(msg, 7); }
 UPB_INLINE bool validate_StringRules_strict(const validate_StringRules *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(56, 56), bool); }
 

From 1723e8ce221a16b330be55aabfb6ae71100d8013 Mon Sep 17 00:00:00 2001
From: "Mark D. Roth" <roth@google.com>
Date: Tue, 4 Aug 2020 08:29:25 -0700
Subject: [PATCH 38/38] Fix use-after-free bug introduced in #23581.

---
 src/core/lib/surface/server.cc | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/core/lib/surface/server.cc b/src/core/lib/surface/server.cc
index d66896a5a88..c94551545d1 100644
--- a/src/core/lib/surface/server.cc
+++ b/src/core/lib/surface/server.cc
@@ -1115,8 +1115,9 @@ void Server::ChannelData::AcceptStream(void* arg, grpc_transport* /*transport*/,
 
 void Server::ChannelData::FinishDestroy(void* cd, grpc_error* /*error*/) {
   auto* chand = static_cast<Server::ChannelData*>(cd);
+  Server* server = chand->server_.get();
   GRPC_CHANNEL_INTERNAL_UNREF(chand->channel_, "server");
-  chand->server_->Unref();
+  server->Unref();
 }
 
 void Server::ChannelData::Destroy() {