MASQUE initial drop
diff --git a/quic/masque/README.md b/quic/masque/README.md
new file mode 100644
index 0000000..6bfc08e
--- /dev/null
+++ b/quic/masque/README.md
@@ -0,0 +1,4 @@
+# MASQUE
+
+The files in this directory implement MASQUE as described in
+<https://tools.ietf.org/html/draft-schinazi-masque>.
diff --git a/quic/masque/masque_client_bin.cc b/quic/masque/masque_client_bin.cc
new file mode 100644
index 0000000..abc4995
--- /dev/null
+++ b/quic/masque/masque_client_bin.cc
@@ -0,0 +1,175 @@
+// 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 <memory>
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "net/third_party/quiche/src/quic/masque/masque_encapsulated_epoll_client.h"
+#include "net/third_party/quiche/src/quic/masque/masque_epoll_client.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/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_system_event_loop.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/tools/fake_proof_verifier.h"
+#include "net/third_party/quiche/src/quic/tools/quic_url.h"
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(bool,
+                              disable_certificate_verification,
+                              false,
+                              "If true, don't verify the server certificate.");
+
+namespace quic {
+
+namespace {
+
+bool SendRequest(MasqueEpollClient* masque_client,
+                 QuicEpollServer* epoll_server,
+                 std::string url_string,
+                 bool disable_certificate_verification) {
+  QuicUrl url(url_string, "https");
+  std::unique_ptr<ProofVerifier> proof_verifier;
+  if (disable_certificate_verification) {
+    proof_verifier = std::make_unique<FakeProofVerifier>();
+  } else {
+    proof_verifier = CreateDefaultProofVerifier(url.host());
+  }
+
+  // Build the client, and try to connect.
+  QuicSocketAddress addr =
+      tools::LookupAddress(url.host(), QuicStrCat(url.port()));
+  if (!addr.IsInitialized()) {
+    QUIC_LOG(ERROR) << "Unable to resolve address: " << url.host();
+    return false;
+  }
+  QuicServerId server_id(url.host(), url.port(),
+                         /*privacy_mode_enabled=*/false);
+  auto client = std::make_unique<MasqueEncapsulatedEpollClient>(
+      addr, server_id, epoll_server, std::move(proof_verifier), masque_client);
+
+  if (client == nullptr) {
+    QUIC_LOG(ERROR) << "Failed to create MasqueEncapsulatedEpollClient for "
+                    << url_string;
+    return false;
+  }
+
+  client->set_initial_max_packet_length(kMasqueMaxEncapsulatedPacketSize);
+  client->set_drop_response_body(false);
+  if (!client->Initialize()) {
+    QUIC_LOG(ERROR) << "Failed to initialize MasqueEncapsulatedEpollClient for "
+                    << url_string;
+    return false;
+  }
+
+  if (!client->Connect()) {
+    QuicErrorCode error = client->session()->error();
+    QUIC_LOG(ERROR) << "Failed to connect with client "
+                    << client->session()->connection()->client_connection_id()
+                    << " server " << client->session()->connection_id()
+                    << " to " << url.HostPort()
+                    << ". Error: " << QuicErrorCodeToString(error);
+    return false;
+  }
+
+  std::cerr << "Connected client "
+            << client->session()->connection()->client_connection_id()
+            << " server " << client->session()->connection_id() << " for "
+            << url_string << std::endl;
+
+  // Construct the string body from flags, if provided.
+  std::string body = "foo";
+
+  // Construct a GET or POST request for supplied URL.
+  spdy::SpdyHeaderBlock header_block;
+  header_block[":method"] = "GET";
+  header_block[":scheme"] = url.scheme();
+  header_block[":authority"] = url.HostPort();
+  header_block[":path"] = url.PathParamsQuery();
+
+  // Make sure to store the response, for later output.
+  client->set_store_response(true);
+
+  // Send the MASQUE init request.
+  client->SendRequestAndWaitForResponse(header_block, body,
+                                        /*fin=*/true);
+
+  if (!client->connected()) {
+    QUIC_LOG(ERROR) << "Request for " << url_string
+                    << " caused connection failure. Error: "
+                    << QuicErrorCodeToString(client->session()->error());
+    return false;
+  }
+
+  const int response_code = client->latest_response_code();
+  if (response_code < 200 || response_code >= 300) {
+    QUIC_LOG(ERROR) << "Request for " << url_string
+                    << " failed with HTTP response code " << response_code;
+    return false;
+  }
+
+  std::string response_body = client->latest_response_body();
+  std::cerr << "Request succeeded for " << url_string << std::endl
+            << response_body << std::endl;
+
+  return true;
+}
+
+int RunMasqueClient(int argc, char* argv[]) {
+  QuicSystemEventLoop event_loop("masque_client");
+  const char* usage = "Usage: masque_client [options] <url>";
+
+  // All non-flag arguments should be interpreted as URLs to fetch.
+  std::vector<std::string> urls = QuicParseCommandLineFlags(usage, argc, argv);
+  if (urls.empty()) {
+    QuicPrintCommandLineFlagHelp(usage);
+    return 1;
+  }
+
+  const bool disable_certificate_verification =
+      GetQuicFlag(FLAGS_disable_certificate_verification);
+  QuicEpollServer epoll_server;
+
+  QuicUrl masque_url(urls[0], "https");
+  if (masque_url.host().empty()) {
+    masque_url = QuicUrl(QuicStrCat("https://", urls[0]), "https");
+  }
+  if (masque_url.host().empty()) {
+    QUIC_LOG(ERROR) << "Failed to parse MASQUE server address " << urls[0];
+    return 1;
+  }
+  std::unique_ptr<ProofVerifier> proof_verifier;
+  if (disable_certificate_verification) {
+    proof_verifier = std::make_unique<FakeProofVerifier>();
+  } else {
+    proof_verifier = CreateDefaultProofVerifier(masque_url.host());
+  }
+  std::unique_ptr<MasqueEpollClient> masque_client =
+      MasqueEpollClient::Create(masque_url.host(), masque_url.port(),
+                                &epoll_server, std::move(proof_verifier));
+  if (masque_client == nullptr) {
+    return 1;
+  }
+
+  std::cerr << "MASQUE is connected " << masque_client->connection_id()
+            << std::endl;
+
+  for (size_t i = 1; i < urls.size(); ++i) {
+    if (!SendRequest(masque_client.get(), &epoll_server, urls[i],
+                     disable_certificate_verification)) {
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+}  // namespace
+
+}  // namespace quic
+
+int main(int argc, char* argv[]) {
+  return quic::RunMasqueClient(argc, argv);
+}
diff --git a/quic/masque/masque_client_session.cc b/quic/masque/masque_client_session.cc
new file mode 100644
index 0000000..a2ebe46
--- /dev/null
+++ b/quic/masque/masque_client_session.cc
@@ -0,0 +1,96 @@
+// 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_client_session.h"
+
+namespace quic {
+
+MasqueClientSession::MasqueClientSession(
+    const QuicConfig& config,
+    const ParsedQuicVersionVector& supported_versions,
+    QuicConnection* connection,
+    const QuicServerId& server_id,
+    QuicCryptoClientConfig* crypto_config,
+    QuicClientPushPromiseIndex* push_promise_index,
+    Visitor* owner)
+    : QuicSpdyClientSession(config,
+                            supported_versions,
+                            connection,
+                            server_id,
+                            crypto_config,
+                            push_promise_index),
+      owner_(owner),
+      compression_engine_(this) {}
+
+void MasqueClientSession::OnMessageReceived(QuicStringPiece message) {
+  QUIC_DVLOG(1) << "Received message of length " << message.length();
+
+  QuicConnectionId client_connection_id, server_connection_id;
+  QuicSocketAddress server_socket_address;
+  std::string packet;
+  bool version_present;
+  if (!compression_engine_.DecompressMessage(
+          message, &client_connection_id, &server_connection_id,
+          &server_socket_address, &packet, &version_present)) {
+    return;
+  }
+
+  auto connection_id_registration =
+      client_connection_id_registrations_.find(client_connection_id);
+  if (connection_id_registration == client_connection_id_registrations_.end()) {
+    QUIC_DLOG(ERROR) << "MasqueClientSession failed to dispatch "
+                     << client_connection_id;
+    return;
+  }
+  EncapsulatedClientSession* encapsulated_client_session =
+      connection_id_registration->second;
+  encapsulated_client_session->ProcessPacket(packet, server_socket_address);
+
+  QUIC_DVLOG(1) << "Sent " << packet.length() << " bytes to connection for "
+                << client_connection_id;
+}
+
+void MasqueClientSession::OnMessageAcked(QuicMessageId message_id,
+                                         QuicTime /*receive_timestamp*/) {
+  QUIC_DVLOG(1) << "Received ack for message " << message_id;
+}
+
+void MasqueClientSession::OnMessageLost(QuicMessageId message_id) {
+  QUIC_DVLOG(1) << "We believe message " << message_id << " was lost";
+}
+
+void MasqueClientSession::SendPacket(
+    QuicConnectionId client_connection_id,
+    QuicConnectionId server_connection_id,
+    QuicStringPiece packet,
+    const QuicSocketAddress& server_socket_address) {
+  compression_engine_.CompressAndSendPacket(packet, client_connection_id,
+                                            server_connection_id,
+                                            server_socket_address);
+}
+
+void MasqueClientSession::RegisterConnectionId(
+    QuicConnectionId client_connection_id,
+    EncapsulatedClientSession* encapsulated_client_session) {
+  QUIC_DLOG(INFO) << "Registering " << client_connection_id
+                  << " to encapsulated client";
+  DCHECK(client_connection_id_registrations_.find(client_connection_id) ==
+             client_connection_id_registrations_.end() ||
+         client_connection_id_registrations_[client_connection_id] ==
+             encapsulated_client_session);
+  client_connection_id_registrations_[client_connection_id] =
+      encapsulated_client_session;
+}
+
+void MasqueClientSession::UnregisterConnectionId(
+    QuicConnectionId client_connection_id) {
+  QUIC_DLOG(INFO) << "Unregistering " << client_connection_id;
+  DCHECK(client_connection_id_registrations_.find(client_connection_id) !=
+         client_connection_id_registrations_.end());
+  client_connection_id_registrations_.erase(client_connection_id);
+  owner_->SendClientConnectionIdUnregister(client_connection_id);
+  compression_engine_.UnregisterClientConnectionId(client_connection_id);
+}
+
+}  // namespace quic
diff --git a/quic/masque/masque_client_session.h b/quic/masque/masque_client_session.h
new file mode 100644
index 0000000..9db3862
--- /dev/null
+++ b/quic/masque/masque_client_session.h
@@ -0,0 +1,85 @@
+// 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_CLIENT_SESSION_H_
+#define QUICHE_QUIC_MASQUE_MASQUE_CLIENT_SESSION_H_
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h"
+#include "net/third_party/quiche/src/quic/masque/masque_compression_engine.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// QUIC client session for connection to MASQUE proxy.
+class QUIC_EXPORT_PRIVATE MasqueClientSession : public QuicSpdyClientSession {
+ public:
+  // Visitor interface meant to be implemented by the owner of the
+  // MasqueClientSession instance.
+  class QUIC_EXPORT_PRIVATE Visitor {
+   public:
+    virtual ~Visitor() {}
+
+    // Notifies the visitor that the client connection ID is no longer in use.
+    virtual void SendClientConnectionIdUnregister(
+        QuicConnectionId client_connection_id) = 0;
+  };
+  // Interface meant to be implemented by encapsulated client sessions.
+  class QUIC_EXPORT_PRIVATE EncapsulatedClientSession {
+   public:
+    virtual ~EncapsulatedClientSession() {}
+
+    // Process packet that was just decapsulated.
+    virtual void ProcessPacket(QuicStringPiece packet,
+                               QuicSocketAddress server_socket_address) = 0;
+  };
+
+  MasqueClientSession(const QuicConfig& config,
+                      const ParsedQuicVersionVector& supported_versions,
+                      QuicConnection* connection,
+                      const QuicServerId& server_id,
+                      QuicCryptoClientConfig* crypto_config,
+                      QuicClientPushPromiseIndex* push_promise_index,
+                      Visitor* owner);
+
+  // From QuicSession.
+  void OnMessageReceived(QuicStringPiece message) override;
+
+  void OnMessageAcked(QuicMessageId message_id,
+                      QuicTime receive_timestamp) override;
+
+  void OnMessageLost(QuicMessageId message_id) override;
+
+  // Send encapsulated packet.
+  void SendPacket(QuicConnectionId client_connection_id,
+                  QuicConnectionId server_connection_id,
+                  QuicStringPiece packet,
+                  const QuicSocketAddress& server_socket_address);
+
+  // Register encapsulated client.
+  void RegisterConnectionId(
+      QuicConnectionId client_connection_id,
+      EncapsulatedClientSession* encapsulated_client_session);
+
+  // Unregister encapsulated client.
+  void UnregisterConnectionId(QuicConnectionId client_connection_id);
+
+  // Disallow default constructor, copy, and assign.
+  MasqueClientSession() = delete;
+  MasqueClientSession(const MasqueClientSession&) = delete;
+  MasqueClientSession& operator=(const MasqueClientSession&) = delete;
+
+ private:
+  QuicUnorderedMap<QuicConnectionId,
+                   EncapsulatedClientSession*,
+                   QuicConnectionIdHash>
+      client_connection_id_registrations_;
+  Visitor* owner_;  // Unowned;
+  MasqueCompressionEngine compression_engine_;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_MASQUE_MASQUE_CLIENT_SESSION_H_
diff --git a/quic/masque/masque_compression_engine.cc b/quic/masque/masque_compression_engine.cc
new file mode 100644
index 0000000..99def66
--- /dev/null
+++ b/quic/masque/masque_compression_engine.cc
@@ -0,0 +1,476 @@
+// 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_compression_engine.h"
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+namespace quic {
+
+namespace {
+const QuicDatagramFlowId kFlowId0 = 0;
+
+enum MasqueAddressFamily : uint8_t {
+  MasqueAddressFamilyIPv4 = 4,
+  MasqueAddressFamilyIPv6 = 6,
+};
+
+}  // namespace
+
+MasqueCompressionEngine::MasqueCompressionEngine(QuicSession* masque_session)
+    : masque_session_(masque_session) {
+  if (masque_session_->perspective() == Perspective::IS_SERVER) {
+    next_flow_id_ = 1;
+  } else {
+    next_flow_id_ = 2;
+  }
+}
+
+void MasqueCompressionEngine::CompressAndSendPacket(
+    QuicStringPiece packet,
+    QuicConnectionId client_connection_id,
+    QuicConnectionId server_connection_id,
+    const QuicSocketAddress& server_socket_address) {
+  QUIC_DVLOG(2) << "Compressing client " << client_connection_id << " server "
+                << server_connection_id << "\n"
+                << QuicTextUtils::HexDump(packet);
+  DCHECK(server_socket_address.IsInitialized());
+  if (packet.empty()) {
+    QUIC_BUG << "Tried to send empty packet";
+    return;
+  }
+  QuicDataReader reader(packet.data(), packet.length());
+  uint8_t first_byte;
+  if (!reader.ReadUInt8(&first_byte)) {
+    QUIC_BUG << "Failed to read first_byte";
+    return;
+  }
+  const bool long_header = (first_byte & FLAGS_LONG_HEADER) != 0;
+  bool client_connection_id_present = true, server_connection_id_present = true;
+  QuicConnectionId destination_connection_id, source_connection_id;
+  if (masque_session_->perspective() == Perspective::IS_SERVER) {
+    destination_connection_id = client_connection_id;
+    source_connection_id = server_connection_id;
+    if (!long_header) {
+      server_connection_id_present = false;
+    }
+  } else {
+    destination_connection_id = server_connection_id;
+    source_connection_id = client_connection_id;
+    if (!long_header) {
+      client_connection_id_present = false;
+    }
+  }
+
+  QuicDatagramFlowId flow_id = 0;
+  bool validated = false;
+  for (auto kv : contexts_) {
+    const MasqueCompressionContext& context = kv.second;
+    if (context.server_socket_address != server_socket_address) {
+      continue;
+    }
+    if (client_connection_id_present &&
+        context.client_connection_id != client_connection_id) {
+      continue;
+    }
+    if (server_connection_id_present &&
+        context.server_connection_id != server_connection_id) {
+      continue;
+    }
+    flow_id = kv.first;
+    DCHECK_NE(flow_id, kFlowId0);
+    validated = context.validated;
+    QUIC_DVLOG(1) << "Compressing using " << (validated ? "" : "un")
+                  << "validated flow_id " << flow_id << " to "
+                  << context.server_socket_address << " client "
+                  << context.client_connection_id << " server "
+                  << context.server_connection_id;
+    break;
+  }
+  if (flow_id == 0) {
+    flow_id = GetNextFlowId();
+    QUIC_DVLOG(1) << "Compression assigning new flow_id " << flow_id << " to "
+                  << server_socket_address << " client " << client_connection_id
+                  << " server " << server_connection_id;
+    MasqueCompressionContext context;
+    context.client_connection_id = client_connection_id;
+    context.server_connection_id = server_connection_id;
+    context.server_socket_address = server_socket_address;
+    contexts_[flow_id] = context;
+  }
+  size_t slice_length = packet.length() - destination_connection_id.length();
+  if (long_header) {
+    slice_length -= sizeof(uint8_t) * 2 + source_connection_id.length();
+  }
+  if (validated) {
+    slice_length += QuicDataWriter::GetVarInt62Len(flow_id);
+  } else {
+    slice_length += QuicDataWriter::GetVarInt62Len(kFlowId0) +
+                    QuicDataWriter::GetVarInt62Len(flow_id) + sizeof(uint8_t) +
+                    client_connection_id.length() + sizeof(uint8_t) +
+                    server_connection_id.length() +
+                    sizeof(server_socket_address.port()) + sizeof(uint8_t) +
+                    server_socket_address.host().ToPackedString().length();
+  }
+  QuicMemSlice slice(
+      masque_session_->connection()->helper()->GetStreamSendBufferAllocator(),
+      slice_length);
+  QuicDataWriter writer(slice_length, const_cast<char*>(slice.data()));
+  if (validated) {
+    QUIC_DVLOG(1) << "Compressing using validated flow_id " << flow_id;
+    if (!writer.WriteVarInt62(flow_id)) {
+      QUIC_BUG << "Failed to write flow_id";
+      return;
+    }
+  } else {
+    QUIC_DVLOG(1) << "Compressing using unvalidated flow_id " << flow_id;
+    if (!writer.WriteVarInt62(kFlowId0)) {
+      QUIC_BUG << "Failed to write kFlowId0";
+      return;
+    }
+    if (!writer.WriteVarInt62(flow_id)) {
+      QUIC_BUG << "Failed to write flow_id";
+      return;
+    }
+    if (!writer.WriteLengthPrefixedConnectionId(client_connection_id)) {
+      QUIC_BUG << "Failed to write client_connection_id";
+      return;
+    }
+    if (!writer.WriteLengthPrefixedConnectionId(server_connection_id)) {
+      QUIC_BUG << "Failed to write server_connection_id";
+      return;
+    }
+    if (!writer.WriteUInt16(server_socket_address.port())) {
+      QUIC_BUG << "Failed to write port";
+      return;
+    }
+    QuicIpAddress peer_ip = server_socket_address.host();
+    DCHECK(peer_ip.IsInitialized());
+    std::string peer_ip_bytes = peer_ip.ToPackedString();
+    DCHECK(!peer_ip_bytes.empty());
+    uint8_t address_id;
+    if (peer_ip.address_family() == IpAddressFamily::IP_V6) {
+      address_id = MasqueAddressFamilyIPv6;
+      if (peer_ip_bytes.length() != QuicIpAddress::kIPv6AddressSize) {
+        QUIC_BUG << "Bad IPv6 length " << server_socket_address;
+        return;
+      }
+    } else if (peer_ip.address_family() == IpAddressFamily::IP_V4) {
+      address_id = MasqueAddressFamilyIPv4;
+      if (peer_ip_bytes.length() != QuicIpAddress::kIPv4AddressSize) {
+        QUIC_BUG << "Bad IPv4 length " << server_socket_address;
+        return;
+      }
+    } else {
+      QUIC_BUG << "Unexpected server_socket_address " << server_socket_address;
+      return;
+    }
+    if (!writer.WriteUInt8(address_id)) {
+      QUIC_BUG << "Failed to write address_id";
+      return;
+    }
+    if (!writer.WriteStringPiece(peer_ip_bytes)) {
+      QUIC_BUG << "Failed to write IP address";
+      return;
+    }
+  }
+  if (!writer.WriteUInt8(first_byte)) {
+    QUIC_BUG << "Failed to write first_byte";
+    return;
+  }
+  if (long_header) {
+    QuicVersionLabel version_label;
+    if (!reader.ReadUInt32(&version_label)) {
+      QUIC_DLOG(ERROR) << "Failed to read version";
+      return;
+    }
+    if (!writer.WriteUInt32(version_label)) {
+      QUIC_BUG << "Failed to write version";
+      return;
+    }
+    QuicConnectionId packet_destination_connection_id,
+        packet_source_connection_id;
+    if (!reader.ReadLengthPrefixedConnectionId(
+            &packet_destination_connection_id) ||
+        !reader.ReadLengthPrefixedConnectionId(&packet_source_connection_id)) {
+      QUIC_DLOG(ERROR) << "Failed to parse long header connection IDs";
+      return;
+    }
+    if (packet_destination_connection_id != destination_connection_id) {
+      QUIC_DLOG(ERROR) << "Long header packet's destination_connection_id "
+                       << packet_destination_connection_id
+                       << " does not match expected "
+                       << destination_connection_id;
+      return;
+    }
+    if (packet_source_connection_id != source_connection_id) {
+      QUIC_DLOG(ERROR) << "Long header packet's source_connection_id "
+                       << packet_source_connection_id
+                       << " does not match expected " << source_connection_id;
+      return;
+    }
+  } else {
+    QuicConnectionId packet_destination_connection_id;
+    if (!reader.ReadConnectionId(&packet_destination_connection_id,
+                                 destination_connection_id.length())) {
+      QUIC_DLOG(ERROR)
+          << "Failed to read short header packet's destination_connection_id";
+      return;
+    }
+    if (packet_destination_connection_id != destination_connection_id) {
+      QUIC_DLOG(ERROR) << "Short header packet's destination_connection_id "
+                       << packet_destination_connection_id
+                       << " does not match expected "
+                       << destination_connection_id;
+      return;
+    }
+  }
+  QuicStringPiece packet_payload = reader.ReadRemainingPayload();
+  if (!writer.WriteStringPiece(packet_payload)) {
+    QUIC_BUG << "Failed to write packet_payload";
+    return;
+  }
+  MessageResult message_result =
+      masque_session_->SendMessage(QuicMemSliceSpan(&slice));
+
+  QUIC_DVLOG(1) << "Message result " << message_result;
+}
+
+bool MasqueCompressionEngine::DecompressMessage(
+    QuicStringPiece message,
+    QuicConnectionId* client_connection_id,
+    QuicConnectionId* server_connection_id,
+    QuicSocketAddress* server_socket_address,
+    std::string* packet,
+    bool* version_present) {
+  QUIC_DVLOG(1) << "Decompressing message of length " << message.length();
+  QuicDataReader reader(message);
+  QuicDatagramFlowId flow_id;
+  if (!reader.ReadVarInt62(&flow_id)) {
+    QUIC_DLOG(ERROR) << "Could not read flow_id";
+    return false;
+  }
+  MasqueCompressionContext context;
+  if (flow_id == kFlowId0) {
+    QuicDatagramFlowId new_flow_id;
+    if (!reader.ReadVarInt62(&new_flow_id)) {
+      QUIC_DLOG(ERROR) << "Could not read new_flow_id";
+      return false;
+    }
+    QuicConnectionId new_client_connection_id;
+    if (!reader.ReadLengthPrefixedConnectionId(&new_client_connection_id)) {
+      QUIC_DLOG(ERROR) << "Could not read new_client_connection_id";
+      return false;
+    }
+    QuicConnectionId new_server_connection_id;
+    if (!reader.ReadLengthPrefixedConnectionId(&new_server_connection_id)) {
+      QUIC_DLOG(ERROR) << "Could not read new_server_connection_id";
+      return false;
+    }
+    uint16_t port;
+    if (!reader.ReadUInt16(&port)) {
+      QUIC_DLOG(ERROR) << "Could not read port";
+      return false;
+    }
+    uint8_t address_id;
+    if (!reader.ReadUInt8(&address_id)) {
+      QUIC_DLOG(ERROR) << "Could not read address_id";
+      return false;
+    }
+    size_t ip_bytes_length;
+    if (address_id == MasqueAddressFamilyIPv6) {
+      ip_bytes_length = QuicIpAddress::kIPv6AddressSize;
+    } else if (address_id == MasqueAddressFamilyIPv4) {
+      ip_bytes_length = QuicIpAddress::kIPv4AddressSize;
+    } else {
+      QUIC_DLOG(ERROR) << "Unknown address_id " << static_cast<int>(address_id);
+      return false;
+    }
+    char ip_bytes[QuicIpAddress::kMaxAddressSize];
+    if (!reader.ReadBytes(ip_bytes, ip_bytes_length)) {
+      QUIC_DLOG(ERROR) << "Could not read IP address";
+      return false;
+    }
+    QuicIpAddress ip_address;
+    ip_address.FromPackedString(ip_bytes, ip_bytes_length);
+    if (!ip_address.IsInitialized()) {
+      QUIC_BUG << "Failed to parse IP address";
+      return false;
+    }
+    QuicSocketAddress new_server_socket_address =
+        QuicSocketAddress(ip_address, port);
+    auto context_pair = contexts_.find(new_flow_id);
+    if (context_pair == contexts_.end()) {
+      context.client_connection_id = new_client_connection_id;
+      context.server_connection_id = new_server_connection_id;
+      context.server_socket_address = new_server_socket_address;
+      context.validated = true;
+      contexts_[new_flow_id] = context;
+      QUIC_DVLOG(1) << "Registered new flow_id " << new_flow_id << " to "
+                    << new_server_socket_address << " client "
+                    << new_client_connection_id << " server "
+                    << new_server_connection_id;
+    } else {
+      context = context_pair->second;
+      if (context.client_connection_id != new_client_connection_id) {
+        QUIC_LOG(ERROR)
+            << "Received incorrect context registration for existing flow_id "
+            << new_flow_id << " mismatched client "
+            << context.client_connection_id << " " << new_client_connection_id;
+        return false;
+      }
+      if (context.server_connection_id != new_server_connection_id) {
+        QUIC_LOG(ERROR)
+            << "Received incorrect context registration for existing flow_id "
+            << new_flow_id << " mismatched server "
+            << context.server_connection_id << " " << new_server_connection_id;
+        return false;
+      }
+      if (context.server_socket_address != new_server_socket_address) {
+        QUIC_LOG(ERROR)
+            << "Received incorrect context registration for existing flow_id "
+            << new_flow_id << " mismatched server "
+            << context.server_socket_address << " "
+            << new_server_socket_address;
+        return false;
+      }
+      if (!context.validated) {
+        context.validated = true;
+        contexts_[new_flow_id] = context;
+        QUIC_DLOG(INFO)
+            << "Successfully validated remotely-unvalidated flow_id "
+            << new_flow_id << " to " << new_server_socket_address << " client "
+            << new_client_connection_id << " server "
+            << new_server_connection_id;
+      } else {
+        QUIC_DVLOG(1) << "Decompressing using incoming locally-validated "
+                         "remotely-unvalidated flow_id "
+                      << new_flow_id << " to " << new_server_socket_address
+                      << " client " << new_client_connection_id << " server "
+                      << new_server_connection_id;
+      }
+    }
+  } else {
+    auto context_pair = contexts_.find(flow_id);
+    if (context_pair == contexts_.end()) {
+      QUIC_DLOG(ERROR) << "Received unknown flow_id " << flow_id;
+      return false;
+    }
+    context = context_pair->second;
+
+    if (!context.validated) {
+      context.validated = true;
+      contexts_[flow_id] = context;
+      QUIC_DLOG(INFO) << "Successfully validated remotely-validated flow_id "
+                      << flow_id << " to " << context.server_socket_address
+                      << " client " << context.client_connection_id
+                      << " server " << context.server_connection_id;
+    } else {
+      QUIC_DVLOG(1) << "Decompressing using incoming locally-validated "
+                       "remotely-validated flow_id "
+                    << flow_id << " to " << context.server_socket_address
+                    << " client " << context.client_connection_id << " server "
+                    << context.server_connection_id;
+    }
+  }
+  QuicConnectionId destination_connection_id, source_connection_id;
+  if (masque_session_->perspective() == Perspective::IS_SERVER) {
+    destination_connection_id = context.server_connection_id;
+    source_connection_id = context.client_connection_id;
+  } else {
+    destination_connection_id = context.client_connection_id;
+    source_connection_id = context.server_connection_id;
+  }
+
+  size_t packet_length =
+      reader.BytesRemaining() + destination_connection_id.length();
+  uint8_t first_byte;
+  if (!reader.ReadUInt8(&first_byte)) {
+    QUIC_DLOG(ERROR) << "Failed to read first_byte";
+    return false;
+  }
+  const bool long_header = (first_byte & FLAGS_LONG_HEADER) != 0;
+  if (long_header) {
+    packet_length += sizeof(uint8_t) * 2 + source_connection_id.length();
+  }
+  std::string new_packet(packet_length, 0);
+  QuicDataWriter writer(new_packet.length(), new_packet.data());
+  if (!writer.WriteUInt8(first_byte)) {
+    QUIC_BUG << "Failed to write first_byte";
+    return false;
+  }
+  if (long_header) {
+    QuicVersionLabel version_label;
+    if (!reader.ReadUInt32(&version_label)) {
+      QUIC_DLOG(ERROR) << "Failed to read version";
+      return false;
+    }
+    if (!writer.WriteUInt32(version_label)) {
+      QUIC_BUG << "Failed to write version";
+      return false;
+    }
+    if (!writer.WriteLengthPrefixedConnectionId(destination_connection_id)) {
+      QUIC_BUG << "Failed to write long header destination_connection_id";
+      return false;
+    }
+    if (!writer.WriteLengthPrefixedConnectionId(source_connection_id)) {
+      QUIC_BUG << "Failed to write long header source_connection_id";
+      return false;
+    }
+  } else {
+    if (!writer.WriteConnectionId(destination_connection_id)) {
+      QUIC_BUG << "Failed to write short header destination_connection_id";
+      return false;
+    }
+  }
+  QuicStringPiece payload = reader.ReadRemainingPayload();
+  if (!writer.WriteStringPiece(payload)) {
+    QUIC_BUG << "Failed to write payload";
+    return false;
+  }
+
+  *server_socket_address = context.server_socket_address;
+  *client_connection_id = context.client_connection_id;
+  *server_connection_id = context.server_connection_id;
+  *packet = new_packet;
+  *version_present = long_header;
+
+  QUIC_DVLOG(2) << "Decompressed client " << context.client_connection_id
+                << " server " << context.server_connection_id << "\n"
+                << QuicTextUtils::HexDump(new_packet);
+
+  return true;
+}
+
+QuicDatagramFlowId MasqueCompressionEngine::GetNextFlowId() {
+  const QuicDatagramFlowId next_flow_id = next_flow_id_;
+  next_flow_id_ += 2;
+  return next_flow_id;
+}
+
+void MasqueCompressionEngine::UnregisterClientConnectionId(
+    QuicConnectionId client_connection_id) {
+  std::vector<QuicDatagramFlowId> flow_ids_to_remove;
+  for (auto kv : contexts_) {
+    const MasqueCompressionContext& context = kv.second;
+    if (context.client_connection_id == client_connection_id) {
+      flow_ids_to_remove.push_back(kv.first);
+    }
+  }
+  for (QuicDatagramFlowId flow_id : flow_ids_to_remove) {
+    contexts_.erase(flow_id);
+  }
+}
+
+}  // namespace quic
diff --git a/quic/masque/masque_compression_engine.h b/quic/masque/masque_compression_engine.h
new file mode 100644
index 0000000..a061456
--- /dev/null
+++ b/quic/masque/masque_compression_engine.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_PROTOCOL_H_
+#define QUICHE_QUIC_MASQUE_MASQUE_PROTOCOL_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// MASQUE compression engine used by client and servers.
+// This class allows converting QUIC packets into a compressed form suitable
+// for sending over QUIC DATAGRAM frames. It leverages a flow identifier at the
+// start of each datagram to indicate which compression context was used to
+// compress this packet, or to create new compression contexts.
+class QUIC_EXPORT_PRIVATE MasqueCompressionEngine {
+ public:
+  explicit MasqueCompressionEngine(QuicSession* masque_session);
+
+  // Compress packet and send it over a MASQUE session.
+  void CompressAndSendPacket(QuicStringPiece packet,
+                             QuicConnectionId client_connection_id,
+                             QuicConnectionId server_connection_id,
+                             const QuicSocketAddress& server_socket_address);
+
+  // Decompress received message and place it in |packet|.
+  bool DecompressMessage(QuicStringPiece message,
+                         QuicConnectionId* client_connection_id,
+                         QuicConnectionId* server_connection_id,
+                         QuicSocketAddress* server_socket_address,
+                         std::string* packet,
+                         bool* version_present);
+
+  // Clear all entries referencing |client_connection_id| the from
+  // compression table.
+  void UnregisterClientConnectionId(QuicConnectionId client_connection_id);
+
+  // Disallow default constructor, copy, and assign.
+  MasqueCompressionEngine() = delete;
+  MasqueCompressionEngine(const MasqueCompressionEngine&) = delete;
+  MasqueCompressionEngine& operator=(const MasqueCompressionEngine&) = delete;
+
+ private:
+  struct QUIC_EXPORT_PRIVATE MasqueCompressionContext {
+    QuicConnectionId client_connection_id;
+    QuicConnectionId server_connection_id;
+    QuicSocketAddress server_socket_address;
+    bool validated = false;
+  };
+
+  // Generate a new datagram flow ID.
+  QuicDatagramFlowId GetNextFlowId();
+
+  QuicSession* masque_session_;  // Unowned.
+  QuicUnorderedMap<QuicDatagramFlowId, MasqueCompressionContext> contexts_;
+  QuicDatagramFlowId next_flow_id_;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_MASQUE_MASQUE_PROTOCOL_H_
diff --git a/quic/masque/masque_dispatcher.cc b/quic/masque/masque_dispatcher.cc
new file mode 100644
index 0000000..7a2e74e
--- /dev/null
+++ b/quic/masque/masque_dispatcher.cc
@@ -0,0 +1,82 @@
+// 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) {}
+
+QuicServerSessionBase* MasqueDispatcher::CreateQuicSession(
+    QuicConnectionId connection_id,
+    const QuicSocketAddress& client_address,
+    QuicStringPiece /*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});
+
+  MasqueServerSession* session =
+      new 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();
+  DCHECK(client_connection_id_registrations_.find(client_connection_id) ==
+             client_connection_id_registrations_.end() ||
+         client_connection_id_registrations_[client_connection_id] ==
+             masque_server_session);
+  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..55562de
--- /dev/null
+++ b/quic/masque/masque_dispatcher.h
@@ -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.
+
+#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 MASQUE requests.
+class QUIC_EXPORT_PRIVATE MasqueDispatcher
+    : public QuicSimpleDispatcher,
+      public MasqueServerSession::Visitor {
+ public:
+  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);
+
+  // From QuicSimpleDispatcher.
+  QuicServerSessionBase* CreateQuicSession(
+      QuicConnectionId connection_id,
+      const QuicSocketAddress& client_address,
+      QuicStringPiece 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;
+
+  // Disallow default constructor, copy, and assign.
+  MasqueDispatcher() = delete;
+  MasqueDispatcher(const MasqueDispatcher&) = delete;
+  MasqueDispatcher& operator=(const MasqueDispatcher&) = delete;
+
+ private:
+  MasqueServerBackend* masque_server_backend_;  // Unowned.
+  QuicUnorderedMap<QuicConnectionId, MasqueServerSession*, QuicConnectionIdHash>
+      client_connection_id_registrations_;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_MASQUE_MASQUE_DISPATCHER_H_
diff --git a/quic/masque/masque_encapsulated_client_session.cc b/quic/masque/masque_encapsulated_client_session.cc
new file mode 100644
index 0000000..7f6d6ed
--- /dev/null
+++ b/quic/masque/masque_encapsulated_client_session.cc
@@ -0,0 +1,41 @@
+// 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_encapsulated_client_session.h"
+
+namespace quic {
+
+MasqueEncapsulatedClientSession::MasqueEncapsulatedClientSession(
+    const QuicConfig& config,
+    const ParsedQuicVersionVector& supported_versions,
+    QuicConnection* connection,
+    const QuicServerId& server_id,
+    QuicCryptoClientConfig* crypto_config,
+    QuicClientPushPromiseIndex* push_promise_index,
+    MasqueClientSession* masque_client_session)
+    : QuicSpdyClientSession(config,
+                            supported_versions,
+                            connection,
+                            server_id,
+                            crypto_config,
+                            push_promise_index),
+      masque_client_session_(masque_client_session) {}
+
+void MasqueEncapsulatedClientSession::ProcessPacket(
+    QuicStringPiece packet,
+    QuicSocketAddress server_socket_address) {
+  QuicTime now = connection()->clock()->ApproximateNow();
+  QuicReceivedPacket received_packet(packet.data(), packet.length(), now);
+  connection()->ProcessUdpPacket(connection()->self_address(),
+                                 server_socket_address, received_packet);
+}
+
+void MasqueEncapsulatedClientSession::OnConnectionClosed(
+    const QuicConnectionCloseFrame& /*frame*/,
+    ConnectionCloseSource /*source*/) {
+  masque_client_session_->UnregisterConnectionId(
+      connection()->client_connection_id());
+}
+
+}  // namespace quic
diff --git a/quic/masque/masque_encapsulated_client_session.h b/quic/masque/masque_encapsulated_client_session.h
new file mode 100644
index 0000000..e04d4a5
--- /dev/null
+++ b/quic/masque/masque_encapsulated_client_session.h
@@ -0,0 +1,49 @@
+// 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_ENCAPSULATED_CLIENT_SESSION_H_
+#define QUICHE_QUIC_MASQUE_MASQUE_ENCAPSULATED_CLIENT_SESSION_H_
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h"
+#include "net/third_party/quiche/src/quic/masque/masque_client_session.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// QUIC client session for QUIC encapsulated in MASQUE.
+class QUIC_EXPORT_PRIVATE MasqueEncapsulatedClientSession
+    : public QuicSpdyClientSession,
+      public MasqueClientSession::EncapsulatedClientSession {
+ public:
+  MasqueEncapsulatedClientSession(
+      const QuicConfig& config,
+      const ParsedQuicVersionVector& supported_versions,
+      QuicConnection* connection,
+      const QuicServerId& server_id,
+      QuicCryptoClientConfig* crypto_config,
+      QuicClientPushPromiseIndex* push_promise_index,
+      MasqueClientSession* masque_client_session);
+
+  // From MasqueClientSession::EncapsulatedClientSession.
+  void ProcessPacket(QuicStringPiece packet,
+                     QuicSocketAddress server_socket_address) override;
+
+  // From QuicSession.
+  void OnConnectionClosed(const QuicConnectionCloseFrame& frame,
+                          ConnectionCloseSource source) override;
+
+  // Disallow default constructor, copy, and assign.
+  MasqueEncapsulatedClientSession() = delete;
+  MasqueEncapsulatedClientSession(const MasqueEncapsulatedClientSession&) =
+      delete;
+  MasqueEncapsulatedClientSession& operator=(
+      const MasqueEncapsulatedClientSession&) = delete;
+
+ private:
+  MasqueClientSession* masque_client_session_;  // Unowned.
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_MASQUE_MASQUE_ENCAPSULATED_CLIENT_SESSION_H_
diff --git a/quic/masque/masque_encapsulated_epoll_client.cc b/quic/masque/masque_encapsulated_epoll_client.cc
new file mode 100644
index 0000000..07224d7
--- /dev/null
+++ b/quic/masque/masque_encapsulated_epoll_client.cc
@@ -0,0 +1,126 @@
+// 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_encapsulated_epoll_client.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/masque/masque_client_session.h"
+#include "net/third_party/quiche/src/quic/masque/masque_encapsulated_client_session.h"
+#include "net/third_party/quiche/src/quic/masque/masque_epoll_client.h"
+#include "net/third_party/quiche/src/quic/masque/masque_utils.h"
+
+namespace quic {
+
+namespace {
+
+// Custom packet writer that allows getting all of a connection's outgoing
+// packets.
+class MasquePacketWriter : public QuicPacketWriter {
+ public:
+  MasquePacketWriter() = delete;
+  explicit MasquePacketWriter(MasqueEncapsulatedEpollClient* client)
+      : client_(client) {}
+  WriteResult WritePacket(const char* buffer,
+                          size_t buf_len,
+                          const QuicIpAddress& /*self_address*/,
+                          const QuicSocketAddress& peer_address,
+                          PerPacketOptions* /*options*/) override {
+    DCHECK(peer_address.IsInitialized());
+    QUIC_DVLOG(1) << "MasquePacketWriter trying to write " << buf_len
+                  << " bytes to " << peer_address;
+    QuicStringPiece packet(buffer, buf_len);
+    client_->masque_client()->masque_client_session()->SendPacket(
+        client_->session()->connection()->client_connection_id(),
+        client_->session()->connection()->connection_id(), packet,
+        peer_address);
+    return WriteResult(WRITE_STATUS_OK, buf_len);
+  }
+
+  bool IsWriteBlocked() const override { return false; }
+
+  void SetWritable() override {}
+
+  QuicByteCount GetMaxPacketSize(
+      const QuicSocketAddress& /*peer_address*/) const override {
+    return kMasqueMaxEncapsulatedPacketSize;
+  }
+
+  bool SupportsReleaseTime() const override { return false; }
+
+  bool IsBatchMode() const override { return false; }
+  char* GetNextWriteLocation(
+      const QuicIpAddress& /*self_address*/,
+      const QuicSocketAddress& /*peer_address*/) override {
+    return nullptr;
+  }
+
+  WriteResult Flush() override { return WriteResult(WRITE_STATUS_OK, 0); }
+
+ private:
+  MasqueEncapsulatedEpollClient* client_;  // Unowned.
+};
+
+// Custom network helper that allows injecting a custom packet writer in order
+// to get all of a connection's outgoing packets.
+class MasqueClientEpollNetworkHelper : public QuicClientEpollNetworkHelper {
+ public:
+  MasqueClientEpollNetworkHelper(QuicEpollServer* epoll_server,
+                                 MasqueEncapsulatedEpollClient* client)
+      : QuicClientEpollNetworkHelper(epoll_server, client), client_(client) {}
+  QuicPacketWriter* CreateQuicPacketWriter() override {
+    return new MasquePacketWriter(client_);
+  }
+
+ private:
+  MasqueEncapsulatedEpollClient* client_;  // Unowned.
+};
+
+}  // namespace
+
+MasqueEncapsulatedEpollClient::MasqueEncapsulatedEpollClient(
+    QuicSocketAddress server_address,
+    const QuicServerId& server_id,
+    QuicEpollServer* epoll_server,
+    std::unique_ptr<ProofVerifier> proof_verifier,
+    MasqueEpollClient* masque_client)
+    : QuicClient(
+          server_address,
+          server_id,
+          MasqueSupportedVersions(),
+          MasqueEncapsulatedConfig(),
+          epoll_server,
+          std::make_unique<MasqueClientEpollNetworkHelper>(epoll_server, this),
+          std::move(proof_verifier)),
+      masque_client_(masque_client) {}
+
+MasqueEncapsulatedEpollClient::~MasqueEncapsulatedEpollClient() {
+  masque_client_->masque_client_session()->UnregisterConnectionId(
+      client_connection_id_);
+}
+
+std::unique_ptr<QuicSession>
+MasqueEncapsulatedEpollClient::CreateQuicClientSession(
+    const ParsedQuicVersionVector& supported_versions,
+    QuicConnection* connection) {
+  QUIC_DLOG(INFO) << "Creating MASQUE encapsulated session for "
+                  << connection->connection_id();
+  return std::make_unique<MasqueEncapsulatedClientSession>(
+      *config(), supported_versions, connection, server_id(), crypto_config(),
+      push_promise_index(), masque_client_->masque_client_session());
+}
+
+MasqueEncapsulatedClientSession*
+MasqueEncapsulatedEpollClient::masque_encapsulated_client_session() {
+  return static_cast<MasqueEncapsulatedClientSession*>(QuicClient::session());
+}
+
+QuicConnectionId MasqueEncapsulatedEpollClient::GetClientConnectionId() {
+  if (client_connection_id_.IsEmpty()) {
+    client_connection_id_ = QuicUtils::CreateRandomConnectionId();
+    masque_client_->masque_client_session()->RegisterConnectionId(
+        client_connection_id_, masque_encapsulated_client_session());
+  }
+  return client_connection_id_;
+}
+
+}  // namespace quic
diff --git a/quic/masque/masque_encapsulated_epoll_client.h b/quic/masque/masque_encapsulated_epoll_client.h
new file mode 100644
index 0000000..fe1618a
--- /dev/null
+++ b/quic/masque/masque_encapsulated_epoll_client.h
@@ -0,0 +1,51 @@
+// 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_ENCAPSULATED_EPOLL_CLIENT_H_
+#define QUICHE_QUIC_MASQUE_MASQUE_ENCAPSULATED_EPOLL_CLIENT_H_
+
+#include "net/third_party/quiche/src/quic/masque/masque_encapsulated_client_session.h"
+#include "net/third_party/quiche/src/quic/masque/masque_epoll_client.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/tools/quic_client.h"
+
+namespace quic {
+
+// QUIC client for QUIC encapsulated in MASQUE.
+class QUIC_EXPORT_PRIVATE MasqueEncapsulatedEpollClient : public QuicClient {
+ public:
+  MasqueEncapsulatedEpollClient(QuicSocketAddress server_address,
+                                const QuicServerId& server_id,
+                                QuicEpollServer* epoll_server,
+                                std::unique_ptr<ProofVerifier> proof_verifier,
+                                MasqueEpollClient* masque_client);
+  ~MasqueEncapsulatedEpollClient() override;
+
+  // From QuicClient.
+  std::unique_ptr<QuicSession> CreateQuicClientSession(
+      const ParsedQuicVersionVector& supported_versions,
+      QuicConnection* connection) override;
+
+  QuicConnectionId GetClientConnectionId() override;
+
+  // MASQUE client that this client is encapsulated in.
+  MasqueEpollClient* masque_client() { return masque_client_; }
+
+  // Client session for this client.
+  MasqueEncapsulatedClientSession* masque_encapsulated_client_session();
+
+  // Disallow default constructor, copy, and assign.
+  MasqueEncapsulatedEpollClient() = delete;
+  MasqueEncapsulatedEpollClient(const MasqueEncapsulatedEpollClient&) = delete;
+  MasqueEncapsulatedEpollClient& operator=(
+      const MasqueEncapsulatedEpollClient&) = delete;
+
+ private:
+  MasqueEpollClient* masque_client_;  // Unowned.
+  QuicConnectionId client_connection_id_;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_MASQUE_MASQUE_ENCAPSULATED_EPOLL_CLIENT_H_
diff --git a/quic/masque/masque_epoll_client.cc b/quic/masque/masque_epoll_client.cc
new file mode 100644
index 0000000..eaba97d
--- /dev/null
+++ b/quic/masque/masque_epoll_client.cc
@@ -0,0 +1,129 @@
+// 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_client.h"
+#include "net/third_party/quiche/src/quic/masque/masque_client_session.h"
+#include "net/third_party/quiche/src/quic/masque/masque_utils.h"
+
+namespace quic {
+
+MasqueEpollClient::MasqueEpollClient(
+    QuicSocketAddress server_address,
+    const QuicServerId& server_id,
+    QuicEpollServer* epoll_server,
+    std::unique_ptr<ProofVerifier> proof_verifier,
+    const std::string& authority)
+    : QuicClient(server_address,
+                 server_id,
+                 MasqueSupportedVersions(),
+                 epoll_server,
+                 std::move(proof_verifier)),
+      epoll_server_(epoll_server),
+      authority_(authority) {}
+
+std::unique_ptr<QuicSession> MasqueEpollClient::CreateQuicClientSession(
+    const ParsedQuicVersionVector& supported_versions,
+    QuicConnection* connection) {
+  QUIC_DLOG(INFO) << "Creating MASQUE session for "
+                  << connection->connection_id();
+  return std::make_unique<MasqueClientSession>(
+      *config(), supported_versions, connection, server_id(), crypto_config(),
+      push_promise_index(), this);
+}
+
+MasqueClientSession* MasqueEpollClient::masque_client_session() {
+  return static_cast<MasqueClientSession*>(QuicClient::session());
+}
+
+QuicConnectionId MasqueEpollClient::connection_id() {
+  return masque_client_session()->connection_id();
+}
+
+// static
+std::unique_ptr<MasqueEpollClient> MasqueEpollClient::Create(
+    const std::string& host,
+    int port,
+    QuicEpollServer* epoll_server,
+    std::unique_ptr<ProofVerifier> proof_verifier) {
+  // Build the masque_client, and try to connect.
+  QuicSocketAddress addr = tools::LookupAddress(host, QuicStrCat(port));
+  if (!addr.IsInitialized()) {
+    QUIC_LOG(ERROR) << "Unable to resolve address: " << host;
+    return nullptr;
+  }
+  QuicServerId server_id(host, port,
+                         /*privacy_mode_enabled=*/false);
+  auto masque_client = std::make_unique<MasqueEpollClient>(
+      addr, server_id, epoll_server, std::move(proof_verifier),
+      QuicStrCat(host, ":", port));
+
+  if (masque_client == nullptr) {
+    QUIC_LOG(ERROR) << "Failed to create masque_client";
+    return nullptr;
+  }
+
+  masque_client->set_initial_max_packet_length(kDefaultMaxPacketSize);
+  masque_client->set_drop_response_body(false);
+  if (!masque_client->Initialize()) {
+    QUIC_LOG(ERROR) << "Failed to initialize masque_client";
+    return nullptr;
+  }
+  if (!masque_client->Connect()) {
+    QuicErrorCode error = masque_client->session()->error();
+    QUIC_LOG(ERROR) << "Failed to connect to " << host << ":" << port
+                    << ". Error: " << QuicErrorCodeToString(error);
+    return nullptr;
+  }
+
+  std::string body = "foo";
+
+  // Construct a GET or POST request for supplied URL.
+  spdy::SpdyHeaderBlock header_block;
+  header_block[":method"] = "POST";
+  header_block[":scheme"] = "https";
+  header_block[":authority"] = masque_client->authority_;
+  header_block[":path"] = "/.well-known/masque/init";
+
+  // Make sure to store the response, for later output.
+  masque_client->set_store_response(true);
+
+  // Send the MASQUE init request.
+  masque_client->SendRequestAndWaitForResponse(header_block, body,
+                                               /*fin=*/true);
+
+  if (!masque_client->connected()) {
+    QUIC_LOG(ERROR) << "MASQUE init request caused connection failure. Error: "
+                    << QuicErrorCodeToString(masque_client->session()->error());
+    return nullptr;
+  }
+
+  const int response_code = masque_client->latest_response_code();
+  if (response_code != 200) {
+    QUIC_LOG(ERROR) << "MASQUE init request failed with HTTP response code "
+                    << response_code;
+    return nullptr;
+  }
+  return masque_client;
+}
+
+void MasqueEpollClient::SendClientConnectionIdUnregister(
+    QuicConnectionId client_connection_id) {
+  std::string body(client_connection_id.data(), client_connection_id.length());
+
+  // Construct a GET or POST request for supplied URL.
+  spdy::SpdyHeaderBlock header_block;
+  header_block[":method"] = "POST";
+  header_block[":scheme"] = "https";
+  header_block[":authority"] = authority_;
+  header_block[":path"] = "/.well-known/masque/unregister";
+
+  // Make sure to store the response, for later output.
+  set_store_response(true);
+
+  // Send the MASQUE init request.
+  SendRequestAndWaitForResponse(header_block, body,
+                                /*fin=*/true);
+}
+
+}  // namespace quic
diff --git a/quic/masque/masque_epoll_client.h b/quic/masque/masque_epoll_client.h
new file mode 100644
index 0000000..eb1d3f4
--- /dev/null
+++ b/quic/masque/masque_epoll_client.h
@@ -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.
+
+#ifndef QUICHE_QUIC_MASQUE_MASQUE_EPOLL_CLIENT_H_
+#define QUICHE_QUIC_MASQUE_MASQUE_EPOLL_CLIENT_H_
+
+#include "net/third_party/quiche/src/quic/masque/masque_client_session.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/tools/quic_client.h"
+
+namespace quic {
+
+// QUIC client that implements MASQUE.
+class QUIC_EXPORT_PRIVATE MasqueEpollClient
+    : public QuicClient,
+      public MasqueClientSession::Visitor {
+ public:
+  MasqueEpollClient(QuicSocketAddress server_address,
+                    const QuicServerId& server_id,
+                    QuicEpollServer* epoll_server,
+                    std::unique_ptr<ProofVerifier> proof_verifier,
+                    const std::string& authority);
+
+  // Convenience method to construct a MasqueEpollClient, performs a
+  // synchronous DNS lookup.
+  static std::unique_ptr<MasqueEpollClient> Create(
+      const std::string& host,
+      int port,
+      QuicEpollServer* epoll_server,
+      std::unique_ptr<ProofVerifier> proof_verifier);
+
+  // From QuicClient.
+  std::unique_ptr<QuicSession> CreateQuicClientSession(
+      const ParsedQuicVersionVector& supported_versions,
+      QuicConnection* connection) override;
+
+  // Client session for this client.
+  MasqueClientSession* masque_client_session();
+
+  // Convenience accessor for the underlying connection ID.
+  QuicConnectionId connection_id();
+
+  // Send a MASQUE client connection ID unregister message to the server.
+  void SendClientConnectionIdUnregister(
+      QuicConnectionId client_connection_id) override;
+
+  // Disallow default constructor, copy, and assign.
+  MasqueEpollClient() = delete;
+  MasqueEpollClient(const MasqueEpollClient&) = delete;
+  MasqueEpollClient& operator=(const MasqueEpollClient&) = delete;
+
+ private:
+  QuicEpollServer* epoll_server_;  // Unowned.
+  std::string authority_;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_MASQUE_MASQUE_EPOLL_CLIENT_H_
diff --git a/quic/masque/masque_epoll_server.cc b/quic/masque/masque_epoll_server.cc
new file mode 100644
index 0000000..9b0b0a3
--- /dev/null
+++ b/quic/masque/masque_epoll_server.cc
@@ -0,0 +1,33 @@
+// 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::unique_ptr<QuicEpollConnectionHelper>(new QuicEpollConnectionHelper(
+          epoll_server(), QuicAllocator::BUFFER_POOL)),
+      std::unique_ptr<QuicCryptoServerStream::Helper>(
+          new QuicSimpleCryptoServerStreamHelper()),
+      std::unique_ptr<QuicEpollAlarmFactory>(
+          new 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..d6fb090
--- /dev/null
+++ b/quic/masque/masque_epoll_server.h
@@ -0,0 +1,33 @@
+// 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_EXPORT_PRIVATE MasqueEpollServer : public QuicServer {
+ public:
+  explicit MasqueEpollServer(MasqueServerBackend* masque_server_backend);
+
+  // From QuicServer.
+  QuicDispatcher* CreateQuicDispatcher() override;
+
+  // Disallow default constructor, copy, and assign.
+  MasqueEpollServer() = delete;
+  MasqueEpollServer(const MasqueEpollServer&) = delete;
+  MasqueEpollServer& operator=(const MasqueEpollServer&) = delete;
+
+ 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..7f0a8b0
--- /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/quic/platform/api/quic_str_cat.h"
+
+namespace quic {
+
+namespace {
+
+std::string GetRequestHandlerKey(
+    const QuicSimpleServerBackend::RequestHandler* request_handler) {
+  return QuicStrCat(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..d8f1474
--- /dev/null
+++ b/quic/masque/masque_server_backend.h
@@ -0,0 +1,69 @@
+// 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_EXPORT_PRIVATE MasqueServerBackend : public QuicMemoryCacheBackend {
+ public:
+  // Interface meant to be implemented by the owner of the MasqueServerBackend
+  // instance.
+  class QUIC_EXPORT_PRIVATE 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;
+  };
+
+  MasqueServerBackend(const std::string& server_authority,
+                      const std::string& cache_directory);
+
+  // 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);
+
+  // Disallow default constructor, copy, and assign.
+  MasqueServerBackend() = delete;
+  MasqueServerBackend(const MasqueServerBackend&) = delete;
+  MasqueServerBackend& operator=(const MasqueServerBackend&) = delete;
+
+ 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..9ba475d
--- /dev/null
+++ b/quic/masque/masque_server_bin.cc
@@ -0,0 +1,55 @@
+// 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 <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..eec43b2
--- /dev/null
+++ b/quic/masque/masque_server_session.cc
@@ -0,0 +1,127 @@
+// 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(QuicStringPiece message) {
+  QUIC_DVLOG(1) << "Received message of length " << message.length();
+
+  QuicConnectionId client_connection_id, server_connection_id;
+  QuicSocketAddress server_socket_address;
+  std::string packet;
+  bool version_present;
+  if (!compression_engine_.DecompressMessage(
+          message, &client_connection_id, &server_connection_id,
+          &server_socket_address, &packet, &version_present)) {
+    return;
+  }
+
+  QUIC_DVLOG(1) << "Received packet of length " << packet.length() << " for "
+                << server_socket_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_socket_address, nullptr);
+  QUIC_DVLOG(1) << "Got " << write_result << " for " << packet.length()
+                << " bytes to " << server_socket_address;
+}
+
+void MasqueServerSession::OnMessageAcked(QuicMessageId message_id,
+                                         QuicTime /*receive_timestamp*/) {
+  QUIC_DVLOG(1) << "Received ack for message " << message_id;
+}
+
+void MasqueServerSession::OnMessageLost(QuicMessageId message_id) {
+  QUIC_DVLOG(1) << "We believe message " << 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;
+    }
+  }
+
+  std::string response_body = "foobar_response";
+  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..1252eba
--- /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_EXPORT_PRIVATE MasqueServerSession
+    : public QuicSimpleServerSession,
+      public MasqueServerBackend::BackendClient {
+ public:
+  // Interface meant to be implemented by owner of this MasqueServerSession
+  // instance.
+  class QUIC_EXPORT_PRIVATE 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;
+  };
+
+  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);
+
+  // From QuicSession.
+  void OnMessageReceived(QuicStringPiece 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);
+
+  // Disallow default constructor, copy, and assign.
+  MasqueServerSession() = delete;
+  MasqueServerSession(const MasqueServerSession&) = delete;
+  MasqueServerSession& operator=(const MasqueServerSession&) = delete;
+
+ 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_
diff --git a/quic/masque/masque_utils.cc b/quic/masque/masque_utils.cc
new file mode 100644
index 0000000..6ef88ae
--- /dev/null
+++ b/quic/masque/masque_utils.cc
@@ -0,0 +1,22 @@
+// 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_utils.h"
+
+namespace quic {
+
+ParsedQuicVersionVector MasqueSupportedVersions() {
+  QuicVersionInitializeSupportForIetfDraft();
+  ParsedQuicVersion version(PROTOCOL_TLS1_3, QUIC_VERSION_99);
+  QuicEnableVersion(version);
+  return {version};
+}
+
+QuicConfig MasqueEncapsulatedConfig() {
+  QuicConfig config;
+  config.SetMaxPacketSizeToSend(kMasqueMaxEncapsulatedPacketSize);
+  return config;
+}
+
+}  // namespace quic
diff --git a/quic/masque/masque_utils.h b/quic/masque/masque_utils.h
new file mode 100644
index 0000000..65e8de6
--- /dev/null
+++ b/quic/masque/masque_utils.h
@@ -0,0 +1,25 @@
+// 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_UTILS_H_
+#define QUICHE_QUIC_MASQUE_MASQUE_UTILS_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+
+namespace quic {
+
+// List of QUIC versions that support MASQUE. Currently restricted to IETF QUIC.
+QUIC_EXPORT_PRIVATE ParsedQuicVersionVector MasqueSupportedVersions();
+
+// Default QuicConfig for use with MASQUE. Sets a custom max_packet_size.
+QUIC_EXPORT_PRIVATE QuicConfig MasqueEncapsulatedConfig();
+
+// Maximum packet size for encapsulated connections.
+const QuicByteCount kMasqueMaxEncapsulatedPacketSize = 1300;
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_MASQUE_MASQUE_UTILS_H_