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_