Introduce MASQUE, part 3: server code

This CL introduces the server code for MASQUE as defined by <https://tools.ietf.org/html/draft-schinazi-masque>. Most of the work here is plumbing in order to override the right methods of the QUIC codebase. The meat of the MASQUE protocol work is in the parent cl/278956073.

gfe-relnote: n/a, adds unused code
PiperOrigin-RevId: 286093484
Change-Id: Ia05871196f7ae88ece9703f24bc4032f93220889
diff --git a/quic/masque/masque_dispatcher.cc b/quic/masque/masque_dispatcher.cc
new file mode 100644
index 0000000..749aeba
--- /dev/null
+++ b/quic/masque/masque_dispatcher.cc
@@ -0,0 +1,86 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/masque/masque_dispatcher.h"
+#include "net/third_party/quiche/src/quic/masque/masque_server_session.h"
+
+namespace quic {
+
+MasqueDispatcher::MasqueDispatcher(
+    const QuicConfig* config,
+    const QuicCryptoServerConfig* crypto_config,
+    QuicVersionManager* version_manager,
+    std::unique_ptr<QuicConnectionHelperInterface> helper,
+    std::unique_ptr<QuicCryptoServerStream::Helper> session_helper,
+    std::unique_ptr<QuicAlarmFactory> alarm_factory,
+    MasqueServerBackend* masque_server_backend,
+    uint8_t expected_server_connection_id_length)
+    : QuicSimpleDispatcher(config,
+                           crypto_config,
+                           version_manager,
+                           std::move(helper),
+                           std::move(session_helper),
+                           std::move(alarm_factory),
+                           masque_server_backend,
+                           expected_server_connection_id_length),
+      masque_server_backend_(masque_server_backend) {}
+
+std::unique_ptr<QuicSession> MasqueDispatcher::CreateQuicSession(
+    QuicConnectionId connection_id,
+    const QuicSocketAddress& client_address,
+    quiche::QuicheStringPiece /*alpn*/,
+    const ParsedQuicVersion& version) {
+  // The MasqueServerSession takes ownership of |connection| below.
+  QuicConnection* connection = new QuicConnection(
+      connection_id, client_address, helper(), alarm_factory(), writer(),
+      /*owns_writer=*/false, Perspective::IS_SERVER,
+      ParsedQuicVersionVector{version});
+
+  auto session = std::make_unique<MasqueServerSession>(
+      config(), GetSupportedVersions(), connection, this, this,
+      session_helper(), crypto_config(), compressed_certs_cache(),
+      masque_server_backend_);
+  session->Initialize();
+  return session;
+}
+
+bool MasqueDispatcher::OnFailedToDispatchPacket(
+    const ReceivedPacketInfo& packet_info) {
+  auto connection_id_registration = client_connection_id_registrations_.find(
+      packet_info.destination_connection_id);
+  if (connection_id_registration == client_connection_id_registrations_.end()) {
+    QUIC_DLOG(INFO) << "MasqueDispatcher failed to dispatch " << packet_info;
+    return false;
+  }
+  MasqueServerSession* masque_server_session =
+      connection_id_registration->second;
+  masque_server_session->HandlePacketFromServer(packet_info);
+  return true;
+}
+
+void MasqueDispatcher::RegisterClientConnectionId(
+    QuicConnectionId client_connection_id,
+    MasqueServerSession* masque_server_session) {
+  QUIC_DLOG(INFO) << "Registering encapsulated " << client_connection_id
+                  << " to MASQUE session "
+                  << masque_server_session->connection_id();
+
+  // Make sure we don't try to overwrite an existing registration with a
+  // different session.
+  QUIC_BUG_IF(client_connection_id_registrations_.find(client_connection_id) !=
+                  client_connection_id_registrations_.end() &&
+              client_connection_id_registrations_[client_connection_id] !=
+                  masque_server_session)
+      << "Overwriting existing registration for " << client_connection_id;
+  client_connection_id_registrations_[client_connection_id] =
+      masque_server_session;
+}
+
+void MasqueDispatcher::UnregisterClientConnectionId(
+    QuicConnectionId client_connection_id) {
+  QUIC_DLOG(INFO) << "Unregistering " << client_connection_id;
+  client_connection_id_registrations_.erase(client_connection_id);
+}
+
+}  // namespace quic
diff --git a/quic/masque/masque_dispatcher.h b/quic/masque/masque_dispatcher.h
new file mode 100644
index 0000000..a5fc4b0
--- /dev/null
+++ b/quic/masque/masque_dispatcher.h
@@ -0,0 +1,61 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_MASQUE_MASQUE_DISPATCHER_H_
+#define QUICHE_QUIC_MASQUE_MASQUE_DISPATCHER_H_
+
+#include "net/third_party/quiche/src/quic/masque/masque_server_backend.h"
+#include "net/third_party/quiche/src/quic/masque/masque_server_session.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.h"
+
+namespace quic {
+
+// QUIC dispatcher that handles new MASQUE connections and can proxy traffic
+// between MASQUE clients and QUIC servers.
+class QUIC_NO_EXPORT MasqueDispatcher : public QuicSimpleDispatcher,
+                                        public MasqueServerSession::Visitor {
+ public:
+  explicit MasqueDispatcher(
+      const QuicConfig* config,
+      const QuicCryptoServerConfig* crypto_config,
+      QuicVersionManager* version_manager,
+      std::unique_ptr<QuicConnectionHelperInterface> helper,
+      std::unique_ptr<QuicCryptoServerStream::Helper> session_helper,
+      std::unique_ptr<QuicAlarmFactory> alarm_factory,
+      MasqueServerBackend* masque_server_backend,
+      uint8_t expected_server_connection_id_length);
+
+  // Disallow copy and assign.
+  MasqueDispatcher(const MasqueDispatcher&) = delete;
+  MasqueDispatcher& operator=(const MasqueDispatcher&) = delete;
+
+  // From QuicSimpleDispatcher.
+  std::unique_ptr<QuicSession> CreateQuicSession(
+      QuicConnectionId connection_id,
+      const QuicSocketAddress& client_address,
+      quiche::QuicheStringPiece alpn,
+      const ParsedQuicVersion& version) override;
+
+  bool OnFailedToDispatchPacket(const ReceivedPacketInfo& packet_info) override;
+
+  // From MasqueServerSession::Visitor.
+  void RegisterClientConnectionId(
+      QuicConnectionId client_connection_id,
+      MasqueServerSession* masque_server_session) override;
+
+  void UnregisterClientConnectionId(
+      QuicConnectionId client_connection_id) override;
+
+ private:
+  MasqueServerBackend* masque_server_backend_;  // Unowned.
+  // Mapping from client connection IDs to server sessions, allows routing
+  // incoming packets to the right MASQUE connection.
+  QuicUnorderedMap<QuicConnectionId, MasqueServerSession*, QuicConnectionIdHash>
+      client_connection_id_registrations_;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_MASQUE_MASQUE_DISPATCHER_H_
diff --git a/quic/masque/masque_epoll_server.cc b/quic/masque/masque_epoll_server.cc
new file mode 100644
index 0000000..60adeca
--- /dev/null
+++ b/quic/masque/masque_epoll_server.cc
@@ -0,0 +1,31 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/masque/masque_epoll_server.h"
+#include "net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/masque/masque_dispatcher.h"
+#include "net/third_party/quiche/src/quic/masque/masque_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_default_proof_providers.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h"
+
+namespace quic {
+
+MasqueEpollServer::MasqueEpollServer(MasqueServerBackend* masque_server_backend)
+    : QuicServer(CreateDefaultProofSource(),
+                 masque_server_backend,
+                 MasqueSupportedVersions()),
+      masque_server_backend_(masque_server_backend) {}
+
+QuicDispatcher* MasqueEpollServer::CreateQuicDispatcher() {
+  QuicEpollAlarmFactory alarm_factory(epoll_server());
+  return new MasqueDispatcher(
+      &config(), &crypto_config(), version_manager(),
+      std::make_unique<QuicEpollConnectionHelper>(epoll_server(),
+                                                  QuicAllocator::BUFFER_POOL),
+      std::make_unique<QuicSimpleCryptoServerStreamHelper>(),
+      std::make_unique<QuicEpollAlarmFactory>(epoll_server()),
+      masque_server_backend_, expected_server_connection_id_length());
+}
+
+}  // namespace quic
diff --git a/quic/masque/masque_epoll_server.h b/quic/masque/masque_epoll_server.h
new file mode 100644
index 0000000..8d462e8
--- /dev/null
+++ b/quic/masque/masque_epoll_server.h
@@ -0,0 +1,32 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_MASQUE_MASQUE_EPOLL_SERVER_H_
+#define QUICHE_QUIC_MASQUE_MASQUE_EPOLL_SERVER_H_
+
+#include "net/third_party/quiche/src/quic/masque/masque_server_backend.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/tools/quic_server.h"
+
+namespace quic {
+
+// QUIC server that implements MASQUE.
+class QUIC_NO_EXPORT MasqueEpollServer : public QuicServer {
+ public:
+  explicit MasqueEpollServer(MasqueServerBackend* masque_server_backend);
+
+  // Disallow copy and assign.
+  MasqueEpollServer(const MasqueEpollServer&) = delete;
+  MasqueEpollServer& operator=(const MasqueEpollServer&) = delete;
+
+  // From QuicServer.
+  QuicDispatcher* CreateQuicDispatcher() override;
+
+ private:
+  MasqueServerBackend* masque_server_backend_;  // Unowned.
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_MASQUE_MASQUE_EPOLL_SERVER_H_
diff --git a/quic/masque/masque_server_backend.cc b/quic/masque/masque_server_backend.cc
new file mode 100644
index 0000000..41ef780
--- /dev/null
+++ b/quic/masque/masque_server_backend.cc
@@ -0,0 +1,138 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/masque/masque_server_backend.h"
+#include "net/third_party/quiche/src/common/platform/api/quiche_str_cat.h"
+
+namespace quic {
+
+namespace {
+
+std::string GetRequestHandlerKey(
+    const QuicSimpleServerBackend::RequestHandler* request_handler) {
+  return quiche::QuicheStrCat(request_handler->connection_id().ToString(), "_",
+                              request_handler->stream_id(), "_",
+                              request_handler->peer_host());
+}
+
+}  // namespace
+
+MasqueServerBackend::MasqueServerBackend(const std::string& server_authority,
+                                         const std::string& cache_directory)
+    : server_authority_(server_authority) {
+  if (!cache_directory.empty()) {
+    QuicMemoryCacheBackend::InitializeBackend(cache_directory);
+  }
+}
+
+bool MasqueServerBackend::MaybeHandleMasqueRequest(
+    const spdy::SpdyHeaderBlock& request_headers,
+    const std::string& request_body,
+    QuicSimpleServerBackend::RequestHandler* request_handler) {
+  auto path_pair = request_headers.find(":path");
+  auto method_pair = request_headers.find(":method");
+  auto scheme_pair = request_headers.find(":scheme");
+  if (path_pair == request_headers.end() ||
+      method_pair == request_headers.end() ||
+      scheme_pair == request_headers.end()) {
+    // This request is missing required headers.
+    return false;
+  }
+  spdy::SpdyStringPiece path = path_pair->second;
+  spdy::SpdyStringPiece scheme = scheme_pair->second;
+  spdy::SpdyStringPiece method = method_pair->second;
+  if (scheme != "https" || method != "POST" || request_body.empty()) {
+    // MASQUE requests MUST be a non-empty https POST.
+    return false;
+  }
+
+  if (path.rfind("/.well-known/masque/", 0) != 0) {
+    // This request is not a MASQUE path.
+    return false;
+  }
+  std::string masque_path(path.substr(sizeof("/.well-known/masque/") - 1));
+
+  if (!server_authority_.empty()) {
+    auto authority_pair = request_headers.find(":authority");
+    if (authority_pair == request_headers.end()) {
+      // Cannot enforce missing authority.
+      return false;
+    }
+    spdy::SpdyStringPiece authority = authority_pair->second;
+    if (server_authority_ != authority) {
+      // This request does not match server_authority.
+      return false;
+    }
+  }
+
+  auto backend_client_pair =
+      backend_clients_.find(request_handler->connection_id());
+  if (backend_client_pair == backend_clients_.end()) {
+    QUIC_LOG(ERROR) << "Could not find backend client for "
+                    << GetRequestHandlerKey(request_handler) << " "
+                    << masque_path << request_headers.DebugString();
+    return false;
+  }
+
+  BackendClient* backend_client = backend_client_pair->second;
+
+  std::unique_ptr<QuicBackendResponse> response =
+      backend_client->HandleMasqueRequest(masque_path, request_headers,
+                                          request_body, request_handler);
+  if (response == nullptr) {
+    QUIC_LOG(ERROR) << "Backend client did not process request for "
+                    << GetRequestHandlerKey(request_handler) << " "
+                    << masque_path << request_headers.DebugString();
+    return false;
+  }
+
+  QUIC_DLOG(INFO) << "Sending MASQUE response for "
+                  << GetRequestHandlerKey(request_handler) << " " << masque_path
+                  << request_headers.DebugString();
+
+  request_handler->OnResponseBackendComplete(response.get(), {});
+  active_response_map_[GetRequestHandlerKey(request_handler)] =
+      std::move(response);
+
+  return true;
+}
+
+void MasqueServerBackend::FetchResponseFromBackend(
+    const spdy::SpdyHeaderBlock& request_headers,
+    const std::string& request_body,
+    QuicSimpleServerBackend::RequestHandler* request_handler) {
+  if (MaybeHandleMasqueRequest(request_headers, request_body,
+                               request_handler)) {
+    // Request was handled as a MASQUE request.
+    return;
+  }
+  QUIC_DLOG(INFO) << "Fetching non-MASQUE response for "
+                  << GetRequestHandlerKey(request_handler)
+                  << request_headers.DebugString();
+  QuicMemoryCacheBackend::FetchResponseFromBackend(
+      request_headers, request_body, request_handler);
+}
+
+void MasqueServerBackend::CloseBackendResponseStream(
+    QuicSimpleServerBackend::RequestHandler* request_handler) {
+  QUIC_DLOG(INFO) << "Closing response stream for "
+                  << GetRequestHandlerKey(request_handler);
+  active_response_map_.erase(GetRequestHandlerKey(request_handler));
+  QuicMemoryCacheBackend::CloseBackendResponseStream(request_handler);
+}
+
+void MasqueServerBackend::RegisterBackendClient(QuicConnectionId connection_id,
+                                                BackendClient* backend_client) {
+  QUIC_BUG_IF(backend_clients_.find(connection_id) != backend_clients_.end())
+      << connection_id << " already in backend clients map";
+  backend_clients_[connection_id] = backend_client;
+  QUIC_DLOG(INFO) << "Registering backend client for " << connection_id;
+}
+
+void MasqueServerBackend::RemoveBackendClient(QuicConnectionId connection_id) {
+  backend_clients_.erase(connection_id);
+  QUIC_DLOG(INFO) << "Removing backend client for " << connection_id;
+}
+
+}  // namespace quic
diff --git a/quic/masque/masque_server_backend.h b/quic/masque/masque_server_backend.h
new file mode 100644
index 0000000..78dfbf4
--- /dev/null
+++ b/quic/masque/masque_server_backend.h
@@ -0,0 +1,68 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_MASQUE_MASQUE_SERVER_BACKEND_H_
+#define QUICHE_QUIC_MASQUE_MASQUE_SERVER_BACKEND_H_
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h"
+
+namespace quic {
+
+// QUIC server backend that understands MASQUE requests, but otherwise answers
+// HTTP queries using an in-memory cache.
+class QUIC_NO_EXPORT MasqueServerBackend : public QuicMemoryCacheBackend {
+ public:
+  // Interface meant to be implemented by the owner of the MasqueServerBackend
+  // instance.
+  class QUIC_NO_EXPORT BackendClient {
+   public:
+    virtual std::unique_ptr<QuicBackendResponse> HandleMasqueRequest(
+        const std::string& masque_path,
+        const spdy::SpdyHeaderBlock& request_headers,
+        const std::string& request_body,
+        QuicSimpleServerBackend::RequestHandler* request_handler) = 0;
+    virtual ~BackendClient() = default;
+  };
+
+  explicit MasqueServerBackend(const std::string& server_authority,
+                               const std::string& cache_directory);
+
+  // Disallow copy and assign.
+  MasqueServerBackend(const MasqueServerBackend&) = delete;
+  MasqueServerBackend& operator=(const MasqueServerBackend&) = delete;
+
+  // From QuicMemoryCacheBackend.
+  void FetchResponseFromBackend(
+      const spdy::SpdyHeaderBlock& request_headers,
+      const std::string& request_body,
+      QuicSimpleServerBackend::RequestHandler* request_handler) override;
+
+  void CloseBackendResponseStream(
+      QuicSimpleServerBackend::RequestHandler* request_handler) override;
+
+  // Register backend client that can handle MASQUE requests.
+  void RegisterBackendClient(QuicConnectionId connection_id,
+                             BackendClient* backend_client);
+
+  // Unregister backend client.
+  void RemoveBackendClient(QuicConnectionId connection_id);
+
+ private:
+  // Handle MASQUE request.
+  bool MaybeHandleMasqueRequest(
+      const spdy::SpdyHeaderBlock& request_headers,
+      const std::string& request_body,
+      QuicSimpleServerBackend::RequestHandler* request_handler);
+
+  std::string server_authority_;
+  QuicUnorderedMap<std::string, std::unique_ptr<QuicBackendResponse>>
+      active_response_map_;
+  QuicUnorderedMap<QuicConnectionId, BackendClient*, QuicConnectionIdHash>
+      backend_clients_;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_MASQUE_MASQUE_SERVER_BACKEND_H_
diff --git a/quic/masque/masque_server_bin.cc b/quic/masque/masque_server_bin.cc
new file mode 100644
index 0000000..08ead0c
--- /dev/null
+++ b/quic/masque/masque_server_bin.cc
@@ -0,0 +1,60 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is reponsible for the masque_server binary. It allows testing
+// our MASQUE server code by creating a MASQUE proxy that relays HTTP/3
+// requests to web servers tunnelled over MASQUE connections.
+// e.g.: masque_server
+
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/masque/masque_epoll_server.h"
+#include "net/third_party/quiche/src/quic/masque/masque_server_backend.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t,
+                              port,
+                              9661,
+                              "The port the MASQUE server will listen on.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+    std::string,
+    cache_dir,
+    "",
+    "Specifies the directory used during QuicHttpResponseCache "
+    "construction to seed the cache. Cache directory can be "
+    "generated using `wget -p --save-headers <url>`");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+    std::string,
+    server_authority,
+    "",
+    "Specifies the authority over which the server will accept MASQUE "
+    "requests. Defaults to empty which allows all authorities.");
+
+int main(int argc, char* argv[]) {
+  const char* usage = "Usage: masque_server [options]";
+  std::vector<std::string> non_option_args =
+      quic::QuicParseCommandLineFlags(usage, argc, argv);
+  if (!non_option_args.empty()) {
+    quic::QuicPrintCommandLineFlagHelp(usage);
+    return 0;
+  }
+
+  auto backend = std::make_unique<quic::MasqueServerBackend>(
+      GetQuicFlag(FLAGS_server_authority), GetQuicFlag(FLAGS_cache_dir));
+
+  auto server = std::make_unique<quic::MasqueEpollServer>(backend.get());
+
+  if (!server->CreateUDPSocketAndListen(quic::QuicSocketAddress(
+          quic::QuicIpAddress::Any6(), GetQuicFlag(FLAGS_port)))) {
+    return 1;
+  }
+
+  std::cerr << "Started MASQUE server" << std::endl;
+  server->HandleEventsForever();
+  return 0;
+}
diff --git a/quic/masque/masque_server_session.cc b/quic/masque/masque_server_session.cc
new file mode 100644
index 0000000..a1c0e8e
--- /dev/null
+++ b/quic/masque/masque_server_session.cc
@@ -0,0 +1,128 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/masque/masque_server_session.h"
+
+namespace quic {
+
+MasqueServerSession::MasqueServerSession(
+    const QuicConfig& config,
+    const ParsedQuicVersionVector& supported_versions,
+    QuicConnection* connection,
+    QuicSession::Visitor* visitor,
+    Visitor* owner,
+    QuicCryptoServerStream::Helper* helper,
+    const QuicCryptoServerConfig* crypto_config,
+    QuicCompressedCertsCache* compressed_certs_cache,
+    MasqueServerBackend* masque_server_backend)
+    : QuicSimpleServerSession(config,
+                              supported_versions,
+                              connection,
+                              visitor,
+                              helper,
+                              crypto_config,
+                              compressed_certs_cache,
+                              masque_server_backend),
+      masque_server_backend_(masque_server_backend),
+      owner_(owner),
+      compression_engine_(this) {
+  masque_server_backend_->RegisterBackendClient(connection_id(), this);
+}
+
+void MasqueServerSession::OnMessageReceived(quiche::QuicheStringPiece message) {
+  QUIC_DVLOG(1) << "Received DATAGRAM frame of length " << message.length();
+
+  QuicConnectionId client_connection_id, server_connection_id;
+  QuicSocketAddress server_address;
+  std::string packet;
+  bool version_present;
+  if (!compression_engine_.DecompressDatagram(
+          message, &client_connection_id, &server_connection_id,
+          &server_address, &packet, &version_present)) {
+    return;
+  }
+
+  QUIC_DVLOG(1) << "Received packet of length " << packet.length() << " for "
+                << server_address << " client " << client_connection_id;
+
+  if (version_present) {
+    if (client_connection_id.length() != kQuicDefaultConnectionIdLength) {
+      QUIC_DLOG(ERROR)
+          << "Dropping long header with invalid client_connection_id "
+          << client_connection_id;
+      return;
+    }
+    owner_->RegisterClientConnectionId(client_connection_id, this);
+  }
+
+  WriteResult write_result = connection()->writer()->WritePacket(
+      packet.data(), packet.length(), connection()->self_address().host(),
+      server_address, nullptr);
+  QUIC_DVLOG(1) << "Got " << write_result << " for " << packet.length()
+                << " bytes to " << server_address;
+}
+
+void MasqueServerSession::OnMessageAcked(QuicMessageId message_id,
+                                         QuicTime /*receive_timestamp*/) {
+  QUIC_DVLOG(1) << "Received ack for DATAGRAM frame " << message_id;
+}
+
+void MasqueServerSession::OnMessageLost(QuicMessageId message_id) {
+  QUIC_DVLOG(1) << "We believe DATAGRAM frame " << message_id << " was lost";
+}
+
+void MasqueServerSession::OnConnectionClosed(
+    const QuicConnectionCloseFrame& /*frame*/,
+    ConnectionCloseSource /*source*/) {
+  QUIC_DLOG(INFO) << "Closing connection for " << connection_id();
+  masque_server_backend_->RemoveBackendClient(connection_id());
+}
+
+std::unique_ptr<QuicBackendResponse> MasqueServerSession::HandleMasqueRequest(
+    const std::string& masque_path,
+    const spdy::SpdyHeaderBlock& /*request_headers*/,
+    const std::string& request_body,
+    QuicSimpleServerBackend::RequestHandler* /*request_handler*/) {
+  QUIC_DLOG(INFO) << "MasqueServerSession handling MASQUE request";
+
+  if (masque_path == "init") {
+    if (masque_initialized_) {
+      QUIC_DLOG(ERROR) << "Got second MASQUE init request";
+      return nullptr;
+    }
+    masque_initialized_ = true;
+  } else if (masque_path == "unregister") {
+    QuicConnectionId connection_id(request_body.data(), request_body.length());
+    QUIC_DLOG(INFO) << "Received MASQUE request to unregister "
+                    << connection_id;
+    owner_->UnregisterClientConnectionId(connection_id);
+    compression_engine_.UnregisterClientConnectionId(connection_id);
+  } else {
+    if (!masque_initialized_) {
+      QUIC_DLOG(ERROR) << "Got MASQUE request before init";
+      return nullptr;
+    }
+  }
+
+  // TODO(dschinazi) implement binary protocol sent in response body.
+  const std::string response_body = "";
+  spdy::SpdyHeaderBlock response_headers;
+  response_headers[":status"] = "200";
+  auto response = std::make_unique<QuicBackendResponse>();
+  response->set_response_type(QuicBackendResponse::REGULAR_RESPONSE);
+  response->set_headers(std::move(response_headers));
+  response->set_body(response_body);
+
+  return response;
+}
+
+void MasqueServerSession::HandlePacketFromServer(
+    const ReceivedPacketInfo& packet_info) {
+  QUIC_DVLOG(1) << "MasqueServerSession received " << packet_info;
+  compression_engine_.CompressAndSendPacket(
+      packet_info.packet.AsStringPiece(), packet_info.destination_connection_id,
+      packet_info.source_connection_id, packet_info.peer_address);
+}
+
+}  // namespace quic
diff --git a/quic/masque/masque_server_session.h b/quic/masque/masque_server_session.h
new file mode 100644
index 0000000..efc72b8
--- /dev/null
+++ b/quic/masque/masque_server_session.h
@@ -0,0 +1,77 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_MASQUE_MASQUE_SERVER_SESSION_H_
+#define QUICHE_QUIC_MASQUE_MASQUE_SERVER_SESSION_H_
+
+#include "net/third_party/quiche/src/quic/masque/masque_compression_engine.h"
+#include "net/third_party/quiche/src/quic/masque/masque_server_backend.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_session.h"
+
+namespace quic {
+
+// QUIC server session for connection to MASQUE proxy.
+class QUIC_NO_EXPORT MasqueServerSession
+    : public QuicSimpleServerSession,
+      public MasqueServerBackend::BackendClient {
+ public:
+  // Interface meant to be implemented by owner of this MasqueServerSession
+  // instance.
+  class QUIC_NO_EXPORT Visitor {
+   public:
+    virtual ~Visitor() {}
+    // Register a client connection ID as being handled by this session.
+    virtual void RegisterClientConnectionId(
+        QuicConnectionId client_connection_id,
+        MasqueServerSession* masque_server_session) = 0;
+
+    // Unregister a client connection ID.
+    virtual void UnregisterClientConnectionId(
+        QuicConnectionId client_connection_id) = 0;
+  };
+
+  explicit MasqueServerSession(
+      const QuicConfig& config,
+      const ParsedQuicVersionVector& supported_versions,
+      QuicConnection* connection,
+      QuicSession::Visitor* visitor,
+      Visitor* owner,
+      QuicCryptoServerStream::Helper* helper,
+      const QuicCryptoServerConfig* crypto_config,
+      QuicCompressedCertsCache* compressed_certs_cache,
+      MasqueServerBackend* masque_server_backend);
+
+  // Disallow copy and assign.
+  MasqueServerSession(const MasqueServerSession&) = delete;
+  MasqueServerSession& operator=(const MasqueServerSession&) = delete;
+
+  // From QuicSession.
+  void OnMessageReceived(quiche::QuicheStringPiece message) override;
+  void OnMessageAcked(QuicMessageId message_id,
+                      QuicTime receive_timestamp) override;
+  void OnMessageLost(QuicMessageId message_id) override;
+  void OnConnectionClosed(const QuicConnectionCloseFrame& frame,
+                          ConnectionCloseSource source) override;
+
+  // From MasqueServerBackend::BackendClient.
+  std::unique_ptr<QuicBackendResponse> HandleMasqueRequest(
+      const std::string& masque_path,
+      const spdy::SpdyHeaderBlock& request_headers,
+      const std::string& request_body,
+      QuicSimpleServerBackend::RequestHandler* request_handler) override;
+
+  // Handle packet for client, meant to be called by MasqueDispatcher.
+  void HandlePacketFromServer(const ReceivedPacketInfo& packet_info);
+
+ private:
+  MasqueServerBackend* masque_server_backend_;  // Unowned.
+  Visitor* owner_;                              // Unowned.
+  MasqueCompressionEngine compression_engine_;
+  bool masque_initialized_ = false;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_MASQUE_MASQUE_SERVER_SESSION_H_