summaryrefslogtreecommitdiff
path: root/chromium/net/third_party/quiche/src/quic/tools
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/third_party/quiche/src/quic/tools')
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_backend_response.cc29
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_backend_response.h84
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_client.cc99
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_client.h81
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc353
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_client_base.h364
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_client_bin.cc403
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_client_epoll_network_helper.cc206
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_client_epoll_network_helper.h135
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_client_test.cc120
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.cc419
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h198
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend_test.cc235
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_packet_printer_bin.cc246
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_reject_reason_decoder_bin.cc43
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_server.cc212
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_server.h159
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_server_bin.cc70
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_server_test.cc215
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.cc18
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.h38
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_stream.cc33
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_stream.h34
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.cc34
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h38
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper_test.cc25
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.cc68
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.h52
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h60
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.cc228
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.h154
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session_test.cc846
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.cc341
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h102
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream_test.cc679
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.cc273
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.h216
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.cc111
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.h73
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter_test.cc103
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_url.cc101
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_url.h61
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_url_test.cc157
43 files changed, 7516 insertions, 0 deletions
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_backend_response.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_backend_response.cc
new file mode 100644
index 00000000000..7900683913d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_backend_response.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 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/tools/quic_backend_response.h"
+
+namespace quic {
+
+QuicBackendResponse::ServerPushInfo::ServerPushInfo(
+ QuicUrl request_url,
+ spdy::SpdyHeaderBlock headers,
+ spdy::SpdyPriority priority,
+ std::string body)
+ : request_url(request_url),
+ headers(std::move(headers)),
+ priority(priority),
+ body(body) {}
+
+QuicBackendResponse::ServerPushInfo::ServerPushInfo(const ServerPushInfo& other)
+ : request_url(other.request_url),
+ headers(other.headers.Clone()),
+ priority(other.priority),
+ body(other.body) {}
+
+QuicBackendResponse::QuicBackendResponse() : response_type_(REGULAR_RESPONSE) {}
+
+QuicBackendResponse::~QuicBackendResponse() = default;
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_backend_response.h b/chromium/net/third_party/quiche/src/quic/tools/quic_backend_response.h
new file mode 100644
index 00000000000..4ef0fdc1bad
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_backend_response.h
@@ -0,0 +1,84 @@
+// Copyright 2017 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_TOOLS_QUIC_BACKEND_RESPONSE_H_
+#define QUICHE_QUIC_TOOLS_QUIC_BACKEND_RESPONSE_H_
+
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/tools/quic_url.h"
+
+namespace quic {
+
+// Container for HTTP response header/body pairs
+// fetched by the QuicSimpleServerBackend
+class QuicBackendResponse {
+ public:
+ // A ServerPushInfo contains path of the push request and everything needed in
+ // comprising a response for the push request.
+ struct ServerPushInfo {
+ ServerPushInfo(QuicUrl request_url,
+ spdy::SpdyHeaderBlock headers,
+ spdy::SpdyPriority priority,
+ std::string body);
+ ServerPushInfo(const ServerPushInfo& other);
+
+ QuicUrl request_url;
+ spdy::SpdyHeaderBlock headers;
+ spdy::SpdyPriority priority;
+ std::string body;
+ };
+
+ enum SpecialResponseType {
+ REGULAR_RESPONSE, // Send the headers and body like a server should.
+ CLOSE_CONNECTION, // Close the connection (sending the close packet).
+ IGNORE_REQUEST, // Do nothing, expect the client to time out.
+ BACKEND_ERR_RESPONSE, // There was an error fetching the response from
+ // the backend, for example as a TCP connection
+ // error.
+ INCOMPLETE_RESPONSE, // The server will act as if there is a non-empty
+ // trailer but it will not be sent, as a result, FIN
+ // will not be sent too.
+ STOP_SENDING, // Acts like INCOMPLETE_RESPONSE in that the entire
+ // response is not sent. After sending what is sent,
+ // the server will send a STOP_SENDING.
+ };
+ QuicBackendResponse();
+
+ QuicBackendResponse(const QuicBackendResponse& other) = delete;
+ QuicBackendResponse& operator=(const QuicBackendResponse& other) = delete;
+
+ ~QuicBackendResponse();
+
+ SpecialResponseType response_type() const { return response_type_; }
+ const spdy::SpdyHeaderBlock& headers() const { return headers_; }
+ const spdy::SpdyHeaderBlock& trailers() const { return trailers_; }
+ const QuicStringPiece body() const { return QuicStringPiece(body_); }
+
+ void set_response_type(SpecialResponseType response_type) {
+ response_type_ = response_type;
+ }
+
+ void set_headers(spdy::SpdyHeaderBlock headers) {
+ headers_ = std::move(headers);
+ }
+ void set_trailers(spdy::SpdyHeaderBlock trailers) {
+ trailers_ = std::move(trailers);
+ }
+ void set_body(QuicStringPiece body) {
+ body_.assign(body.data(), body.size());
+ }
+ uint16_t stop_sending_code() const { return stop_sending_code_; }
+ void set_stop_sending_code(uint16_t code) { stop_sending_code_ = code; }
+
+ private:
+ SpecialResponseType response_type_;
+ spdy::SpdyHeaderBlock headers_;
+ spdy::SpdyHeaderBlock trailers_;
+ std::string body_;
+ uint16_t stop_sending_code_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_BACKEND_RESPONSE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_client.cc
new file mode 100644
index 00000000000..b736c71aef4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client.cc
@@ -0,0 +1,99 @@
+// Copyright (c) 2012 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/tools/quic_client.h"
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
+#include "net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/quic/platform/impl/quic_socket_utils.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_client_session.h"
+
+#ifndef SO_RXQ_OVFL
+#define SO_RXQ_OVFL 40
+#endif
+
+namespace quic {
+
+QuicClient::QuicClient(QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicEpollServer* epoll_server,
+ std::unique_ptr<ProofVerifier> proof_verifier)
+ : QuicClient(
+ server_address,
+ server_id,
+ supported_versions,
+ QuicConfig(),
+ epoll_server,
+ QuicWrapUnique(new QuicClientEpollNetworkHelper(epoll_server, this)),
+ std::move(proof_verifier)) {}
+
+QuicClient::QuicClient(
+ QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicEpollServer* epoll_server,
+ std::unique_ptr<QuicClientEpollNetworkHelper> network_helper,
+ std::unique_ptr<ProofVerifier> proof_verifier)
+ : QuicClient(server_address,
+ server_id,
+ supported_versions,
+ QuicConfig(),
+ epoll_server,
+ std::move(network_helper),
+ std::move(proof_verifier)) {}
+
+QuicClient::QuicClient(
+ QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicConfig& config,
+ QuicEpollServer* epoll_server,
+ std::unique_ptr<QuicClientEpollNetworkHelper> network_helper,
+ std::unique_ptr<ProofVerifier> proof_verifier)
+ : QuicSpdyClientBase(
+ server_id,
+ supported_versions,
+ config,
+ new QuicEpollConnectionHelper(epoll_server, QuicAllocator::SIMPLE),
+ new QuicEpollAlarmFactory(epoll_server),
+ std::move(network_helper),
+ std::move(proof_verifier)) {
+ set_server_address(server_address);
+}
+
+QuicClient::~QuicClient() = default;
+
+std::unique_ptr<QuicSession> QuicClient::CreateQuicClientSession(
+ const ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection) {
+ return QuicMakeUnique<QuicSimpleClientSession>(
+ *config(), supported_versions, connection, server_id(), crypto_config(),
+ push_promise_index(), drop_response_body_);
+}
+
+QuicClientEpollNetworkHelper* QuicClient::epoll_network_helper() {
+ return static_cast<QuicClientEpollNetworkHelper*>(network_helper());
+}
+
+const QuicClientEpollNetworkHelper* QuicClient::epoll_network_helper() const {
+ return static_cast<const QuicClientEpollNetworkHelper*>(network_helper());
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client.h b/chromium/net/third_party/quiche/src/quic/tools/quic_client.h
new file mode 100644
index 00000000000..b84c597969a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2012 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.
+
+// A toy client, which connects to a specified port and sends QUIC
+// request to that endpoint.
+
+#ifndef QUICHE_QUIC_TOOLS_QUIC_CLIENT_H_
+#define QUICHE_QUIC_TOOLS_QUIC_CLIENT_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_reader.h"
+#include "net/third_party/quiche/src/quic/core/quic_process_packet_interface.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h"
+#include "net/third_party/quiche/src/quic/tools/quic_client_epoll_network_helper.h"
+#include "net/third_party/quiche/src/quic/tools/quic_spdy_client_base.h"
+
+namespace quic {
+
+class QuicServerId;
+
+namespace test {
+class QuicClientPeer;
+} // namespace test
+
+class QuicClient : public QuicSpdyClientBase {
+ public:
+ // This will create its own QuicClientEpollNetworkHelper.
+ QuicClient(QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicEpollServer* epoll_server,
+ std::unique_ptr<ProofVerifier> proof_verifier);
+ // This will take ownership of a passed in network primitive.
+ QuicClient(QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicEpollServer* epoll_server,
+ std::unique_ptr<QuicClientEpollNetworkHelper> network_helper,
+ std::unique_ptr<ProofVerifier> proof_verifier);
+ QuicClient(QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicConfig& config,
+ QuicEpollServer* epoll_server,
+ std::unique_ptr<QuicClientEpollNetworkHelper> network_helper,
+ std::unique_ptr<ProofVerifier> proof_verifier);
+ QuicClient(const QuicClient&) = delete;
+ QuicClient& operator=(const QuicClient&) = delete;
+
+ ~QuicClient() override;
+
+ std::unique_ptr<QuicSession> CreateQuicClientSession(
+ const ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection) override;
+
+ // Exposed for the quic client test.
+ int GetLatestFD() const { return epoll_network_helper()->GetLatestFD(); }
+
+ QuicClientEpollNetworkHelper* epoll_network_helper();
+ const QuicClientEpollNetworkHelper* epoll_network_helper() const;
+
+ void set_drop_response_body(bool drop_response_body) {
+ drop_response_body_ = drop_response_body;
+ }
+
+ private:
+ friend class test::QuicClientPeer;
+ bool drop_response_body_ = false;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_CLIENT_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc
new file mode 100644
index 00000000000..db0503c5f9b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc
@@ -0,0 +1,353 @@
+// Copyright (c) 2015 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/tools/quic_client_base.h"
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.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_text_utils.h"
+
+namespace quic {
+
+QuicClientBase::NetworkHelper::~NetworkHelper() = default;
+
+QuicClientBase::QuicClientBase(
+ const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicConfig& config,
+ QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory,
+ std::unique_ptr<NetworkHelper> network_helper,
+ std::unique_ptr<ProofVerifier> proof_verifier)
+ : server_id_(server_id),
+ initialized_(false),
+ local_port_(0),
+ config_(config),
+ crypto_config_(std::move(proof_verifier),
+ TlsClientHandshaker::CreateSslCtx()),
+ helper_(helper),
+ alarm_factory_(alarm_factory),
+ supported_versions_(supported_versions),
+ initial_max_packet_length_(0),
+ num_stateless_rejects_received_(0),
+ num_sent_client_hellos_(0),
+ connection_error_(QUIC_NO_ERROR),
+ connected_or_attempting_connect_(false),
+ network_helper_(std::move(network_helper)) {}
+
+QuicClientBase::~QuicClientBase() = default;
+
+bool QuicClientBase::Initialize() {
+ num_sent_client_hellos_ = 0;
+ num_stateless_rejects_received_ = 0;
+ connection_error_ = QUIC_NO_ERROR;
+ connected_or_attempting_connect_ = false;
+
+ // If an initial flow control window has not explicitly been set, then use the
+ // same values that Chrome uses.
+ const uint32_t kSessionMaxRecvWindowSize = 15 * 1024 * 1024; // 15 MB
+ const uint32_t kStreamMaxRecvWindowSize = 6 * 1024 * 1024; // 6 MB
+ if (config()->GetInitialStreamFlowControlWindowToSend() ==
+ kMinimumFlowControlSendWindow) {
+ config()->SetInitialStreamFlowControlWindowToSend(kStreamMaxRecvWindowSize);
+ }
+ if (config()->GetInitialSessionFlowControlWindowToSend() ==
+ kMinimumFlowControlSendWindow) {
+ config()->SetInitialSessionFlowControlWindowToSend(
+ kSessionMaxRecvWindowSize);
+ }
+
+ if (!network_helper_->CreateUDPSocketAndBind(server_address_,
+ bind_to_address_, local_port_)) {
+ return false;
+ }
+
+ initialized_ = true;
+ return true;
+}
+
+bool QuicClientBase::Connect() {
+ // Attempt multiple connects until the maximum number of client hellos have
+ // been sent.
+ while (!connected() &&
+ GetNumSentClientHellos() <= QuicCryptoClientStream::kMaxClientHellos) {
+ StartConnect();
+ while (EncryptionBeingEstablished()) {
+ WaitForEvents();
+ }
+ if (GetQuicReloadableFlag(enable_quic_stateless_reject_support) &&
+ connected()) {
+ // Resend any previously queued data.
+ ResendSavedData();
+ }
+ ParsedQuicVersion version = UnsupportedQuicVersion();
+ if (session() != nullptr &&
+ session()->error() != QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT &&
+ !CanReconnectWithDifferentVersion(&version)) {
+ // We've successfully created a session but we're not connected, and there
+ // is no stateless reject to recover from and cannot try to reconnect with
+ // different version. Give up trying.
+ break;
+ }
+ }
+ if (!connected() &&
+ GetNumSentClientHellos() > QuicCryptoClientStream::kMaxClientHellos &&
+ session() != nullptr &&
+ session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
+ // The overall connection failed due too many stateless rejects.
+ set_connection_error(QUIC_CRYPTO_TOO_MANY_REJECTS);
+ }
+ return session()->connection()->connected();
+}
+
+void QuicClientBase::StartConnect() {
+ DCHECK(initialized_);
+ DCHECK(!connected());
+ QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();
+ ParsedQuicVersion mutual_version = UnsupportedQuicVersion();
+ const bool can_reconnect_with_different_version =
+ CanReconnectWithDifferentVersion(&mutual_version);
+ if (connected_or_attempting_connect()) {
+ // If the last error was not a stateless reject, then the queued up data
+ // does not need to be resent.
+ // Keep queued up data if client can try to connect with a different
+ // version.
+ if (session()->error() != QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT &&
+ !can_reconnect_with_different_version) {
+ ClearDataToResend();
+ }
+ // Before we destroy the last session and create a new one, gather its stats
+ // and update the stats for the overall connection.
+ UpdateStats();
+ }
+
+ session_ = CreateQuicClientSession(
+ supported_versions(),
+ new QuicConnection(GetNextConnectionId(), server_address(), helper(),
+ alarm_factory(), writer,
+ /* owns_writer= */ false, Perspective::IS_CLIENT,
+ can_reconnect_with_different_version
+ ? ParsedQuicVersionVector{mutual_version}
+ : supported_versions()));
+ if (initial_max_packet_length_ != 0) {
+ session()->connection()->SetMaxPacketLength(initial_max_packet_length_);
+ }
+ // Reset |writer()| after |session()| so that the old writer outlives the old
+ // session.
+ set_writer(writer);
+ InitializeSession();
+ set_connected_or_attempting_connect(true);
+}
+
+void QuicClientBase::InitializeSession() {
+ session()->Initialize();
+}
+
+void QuicClientBase::Disconnect() {
+ DCHECK(initialized_);
+
+ initialized_ = false;
+ if (connected()) {
+ session()->connection()->CloseConnection(
+ QUIC_PEER_GOING_AWAY, "Client disconnecting",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ }
+
+ ClearDataToResend();
+
+ network_helper_->CleanUpAllUDPSockets();
+}
+
+ProofVerifier* QuicClientBase::proof_verifier() const {
+ return crypto_config_.proof_verifier();
+}
+
+bool QuicClientBase::EncryptionBeingEstablished() {
+ return !session_->IsEncryptionEstablished() &&
+ session_->connection()->connected();
+}
+
+bool QuicClientBase::WaitForEvents() {
+ DCHECK(connected());
+
+ network_helper_->RunEventLoop();
+
+ DCHECK(session() != nullptr);
+ ParsedQuicVersion version = UnsupportedQuicVersion();
+ if (!connected() &&
+ (session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT ||
+ CanReconnectWithDifferentVersion(&version))) {
+ if (session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
+ DCHECK(GetQuicReloadableFlag(enable_quic_stateless_reject_support));
+ QUIC_DLOG(INFO) << "Detected stateless reject while waiting for events. "
+ << "Attempting to reconnect.";
+ } else {
+ QUIC_DLOG(INFO) << "Can reconnect with version: " << version
+ << ", attempting to reconnect.";
+ }
+ Connect();
+ }
+
+ return HasActiveRequests();
+}
+
+bool QuicClientBase::MigrateSocket(const QuicIpAddress& new_host) {
+ return MigrateSocketWithSpecifiedPort(new_host, local_port_);
+}
+
+bool QuicClientBase::MigrateSocketWithSpecifiedPort(
+ const QuicIpAddress& new_host,
+ int port) {
+ if (!connected()) {
+ return false;
+ }
+
+ network_helper_->CleanUpAllUDPSockets();
+
+ set_bind_to_address(new_host);
+ if (!network_helper_->CreateUDPSocketAndBind(server_address_,
+ bind_to_address_, port)) {
+ return false;
+ }
+
+ session()->connection()->SetSelfAddress(
+ network_helper_->GetLatestClientAddress());
+
+ QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();
+ set_writer(writer);
+ session()->connection()->SetQuicPacketWriter(writer, false);
+
+ return true;
+}
+
+bool QuicClientBase::ChangeEphemeralPort() {
+ auto current_host = network_helper_->GetLatestClientAddress().host();
+ return MigrateSocketWithSpecifiedPort(current_host, 0 /*any ephemeral port*/);
+}
+
+QuicSession* QuicClientBase::session() {
+ return session_.get();
+}
+
+QuicClientBase::NetworkHelper* QuicClientBase::network_helper() {
+ return network_helper_.get();
+}
+
+const QuicClientBase::NetworkHelper* QuicClientBase::network_helper() const {
+ return network_helper_.get();
+}
+
+void QuicClientBase::WaitForStreamToClose(QuicStreamId id) {
+ DCHECK(connected());
+
+ while (connected() && !session_->IsClosedStream(id)) {
+ WaitForEvents();
+ }
+}
+
+bool QuicClientBase::WaitForCryptoHandshakeConfirmed() {
+ DCHECK(connected());
+
+ while (connected() && !session_->IsCryptoHandshakeConfirmed()) {
+ WaitForEvents();
+ }
+
+ // If the handshake fails due to a timeout, the connection will be closed.
+ QUIC_LOG_IF(ERROR, !connected()) << "Handshake with server failed.";
+ return connected();
+}
+
+bool QuicClientBase::connected() const {
+ return session_.get() && session_->connection() &&
+ session_->connection()->connected();
+}
+
+bool QuicClientBase::goaway_received() const {
+ return session_ != nullptr && session_->goaway_received();
+}
+
+int QuicClientBase::GetNumSentClientHellos() {
+ // If we are not actively attempting to connect, the session object
+ // corresponds to the previous connection and should not be used.
+ const int current_session_hellos = !connected_or_attempting_connect_
+ ? 0
+ : GetNumSentClientHellosFromSession();
+ return num_sent_client_hellos_ + current_session_hellos;
+}
+
+void QuicClientBase::UpdateStats() {
+ num_sent_client_hellos_ += GetNumSentClientHellosFromSession();
+ if (session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
+ ++num_stateless_rejects_received_;
+ }
+}
+
+int QuicClientBase::GetNumReceivedServerConfigUpdates() {
+ // If we are not actively attempting to connect, the session object
+ // corresponds to the previous connection and should not be used.
+ // We do not need to take stateless rejects into account, since we
+ // don't expect any scup messages to be sent during a
+ // statelessly-rejected connection.
+ return !connected_or_attempting_connect_
+ ? 0
+ : GetNumReceivedServerConfigUpdatesFromSession();
+}
+
+QuicErrorCode QuicClientBase::connection_error() const {
+ // Return the high-level error if there was one. Otherwise, return the
+ // connection error from the last session.
+ if (connection_error_ != QUIC_NO_ERROR) {
+ return connection_error_;
+ }
+ if (session_ == nullptr) {
+ return QUIC_NO_ERROR;
+ }
+ return session_->error();
+}
+
+QuicConnectionId QuicClientBase::GetNextConnectionId() {
+ QuicConnectionId server_designated_id = GetNextServerDesignatedConnectionId();
+ return !server_designated_id.IsEmpty() ? server_designated_id
+ : GenerateNewConnectionId();
+}
+
+QuicConnectionId QuicClientBase::GetNextServerDesignatedConnectionId() {
+ QuicCryptoClientConfig::CachedState* cached =
+ crypto_config_.LookupOrCreate(server_id_);
+ // If the cached state indicates that we should use a server-designated
+ // connection ID, then return that connection ID.
+ CHECK(cached != nullptr) << "QuicClientCryptoConfig::LookupOrCreate returned "
+ << "unexpected nullptr.";
+ return cached->has_server_designated_connection_id()
+ ? cached->GetNextServerDesignatedConnectionId()
+ : EmptyQuicConnectionId();
+}
+
+QuicConnectionId QuicClientBase::GenerateNewConnectionId() {
+ return QuicUtils::CreateRandomConnectionId();
+}
+
+bool QuicClientBase::CanReconnectWithDifferentVersion(
+ ParsedQuicVersion* version) const {
+ if (session_ == nullptr || session_->connection() == nullptr ||
+ session_->error() != QUIC_INVALID_VERSION ||
+ session_->connection()->server_supported_versions().empty()) {
+ return false;
+ }
+ for (const auto& client_version : supported_versions_) {
+ if (QuicContainsValue(session_->connection()->server_supported_versions(),
+ client_version)) {
+ *version = client_version;
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.h b/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.h
new file mode 100644
index 00000000000..6ad326eb4dc
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.h
@@ -0,0 +1,364 @@
+// Copyright (c) 2015 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.
+
+// A base class for the toy client, which connects to a specified port and sends
+// QUIC request to that endpoint.
+
+#ifndef QUICHE_QUIC_TOOLS_QUIC_CLIENT_BASE_H_
+#define QUICHE_QUIC_TOOLS_QUIC_CLIENT_BASE_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_macros.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 {
+
+class ProofVerifier;
+class QuicServerId;
+
+// QuicClientBase handles establishing a connection to the passed in
+// server id, including ensuring that it supports the passed in versions
+// and config.
+// Subclasses derived from this class are responsible for creating the
+// actual QuicSession instance, as well as defining functions that
+// create and run the underlying network transport.
+class QuicClientBase {
+ public:
+ // An interface to various network events that the QuicClient will need to
+ // interact with.
+ class NetworkHelper {
+ public:
+ virtual ~NetworkHelper();
+
+ // Runs one iteration of the event loop.
+ virtual void RunEventLoop() = 0;
+
+ // Used during initialization: creates the UDP socket FD, sets socket
+ // options, and binds the socket to our address.
+ virtual bool CreateUDPSocketAndBind(QuicSocketAddress server_address,
+ QuicIpAddress bind_to_address,
+ int bind_to_port) = 0;
+
+ // Unregister and close all open UDP sockets.
+ virtual void CleanUpAllUDPSockets() = 0;
+
+ // If the client has at least one UDP socket, return address of the latest
+ // created one. Otherwise, return an empty socket address.
+ virtual QuicSocketAddress GetLatestClientAddress() const = 0;
+
+ // Creates a packet writer to be used for the next connection.
+ virtual QuicPacketWriter* CreateQuicPacketWriter() = 0;
+ };
+
+ QuicClientBase(const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicConfig& config,
+ QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory,
+ std::unique_ptr<NetworkHelper> network_helper,
+ std::unique_ptr<ProofVerifier> proof_verifier);
+ QuicClientBase(const QuicClientBase&) = delete;
+ QuicClientBase& operator=(const QuicClientBase&) = delete;
+
+ virtual ~QuicClientBase();
+
+ // Initializes the client to create a connection. Should be called exactly
+ // once before calling StartConnect or Connect. Returns true if the
+ // initialization succeeds, false otherwise.
+ virtual bool Initialize();
+
+ // "Connect" to the QUIC server, including performing synchronous crypto
+ // handshake.
+ bool Connect();
+
+ // Start the crypto handshake. This can be done in place of the synchronous
+ // Connect(), but callers are responsible for making sure the crypto handshake
+ // completes.
+ void StartConnect();
+
+ // Calls session()->Initialize(). Subclasses may override this if any extra
+ // initialization needs to be done. Subclasses should expect that session()
+ // is non-null and valid.
+ virtual void InitializeSession();
+
+ // Disconnects from the QUIC server.
+ void Disconnect();
+
+ // Returns true if the crypto handshake has yet to establish encryption.
+ // Returns false if encryption is active (even if the server hasn't confirmed
+ // the handshake) or if the connection has been closed.
+ bool EncryptionBeingEstablished();
+
+ // Wait for events until the stream with the given ID is closed.
+ void WaitForStreamToClose(QuicStreamId id);
+
+ // Wait for events until the handshake is confirmed.
+ // Returns true if the crypto handshake succeeds, false otherwise.
+ bool WaitForCryptoHandshakeConfirmed() QUIC_MUST_USE_RESULT;
+
+ // Wait up to 50ms, and handle any events which occur.
+ // Returns true if there are any outstanding requests.
+ bool WaitForEvents();
+
+ // Migrate to a new socket (new_host) during an active connection.
+ bool MigrateSocket(const QuicIpAddress& new_host);
+
+ // Migrate to a new socket (new_host, port) during an active connection.
+ bool MigrateSocketWithSpecifiedPort(const QuicIpAddress& new_host, int port);
+
+ // Open a new socket to change to a new ephemeral port.
+ bool ChangeEphemeralPort();
+
+ QuicSession* session();
+
+ bool connected() const;
+ bool goaway_received() const;
+
+ const QuicServerId& server_id() const { return server_id_; }
+
+ // This should only be set before the initial Connect()
+ void set_server_id(const QuicServerId& server_id) { server_id_ = server_id; }
+
+ void SetUserAgentID(const std::string& user_agent_id) {
+ crypto_config_.set_user_agent_id(user_agent_id);
+ }
+
+ // SetChannelIDSource sets a ChannelIDSource that will be called, when the
+ // server supports channel IDs, to obtain a channel ID for signing a message
+ // proving possession of the channel ID.
+ void SetChannelIDSource(std::unique_ptr<ChannelIDSource> source) {
+ crypto_config_.SetChannelIDSource(std::move(source));
+ }
+
+ const ParsedQuicVersionVector& supported_versions() const {
+ return supported_versions_;
+ }
+
+ void SetSupportedVersions(const ParsedQuicVersionVector& versions) {
+ supported_versions_ = versions;
+ }
+
+ QuicConfig* config() { return &config_; }
+
+ QuicCryptoClientConfig* crypto_config() { return &crypto_config_; }
+
+ // Change the initial maximum packet size of the connection. Has to be called
+ // before Connect()/StartConnect() in order to have any effect.
+ void set_initial_max_packet_length(QuicByteCount initial_max_packet_length) {
+ initial_max_packet_length_ = initial_max_packet_length;
+ }
+
+ int num_stateless_rejects_received() const {
+ return num_stateless_rejects_received_;
+ }
+
+ // The number of client hellos sent, taking stateless rejects into
+ // account. In the case of a stateless reject, the initial
+ // connection object may be torn down and a new one created. The
+ // user cannot rely upon the latest connection object to get the
+ // total number of client hellos sent, and should use this function
+ // instead.
+ int GetNumSentClientHellos();
+
+ // Gather the stats for the last session and update the stats for the overall
+ // connection.
+ void UpdateStats();
+
+ // The number of server config updates received. We assume no
+ // updates can be sent during a previously, statelessly rejected
+ // connection, so only the latest session is taken into account.
+ int GetNumReceivedServerConfigUpdates();
+
+ // Returns any errors that occurred at the connection-level (as
+ // opposed to the session-level). When a stateless reject occurs,
+ // the error of the last session may not reflect the overall state
+ // of the connection.
+ QuicErrorCode connection_error() const;
+ void set_connection_error(QuicErrorCode connection_error) {
+ connection_error_ = connection_error;
+ }
+
+ bool connected_or_attempting_connect() const {
+ return connected_or_attempting_connect_;
+ }
+ void set_connected_or_attempting_connect(
+ bool connected_or_attempting_connect) {
+ connected_or_attempting_connect_ = connected_or_attempting_connect;
+ }
+
+ QuicPacketWriter* writer() { return writer_.get(); }
+ void set_writer(QuicPacketWriter* writer) {
+ if (writer_.get() != writer) {
+ writer_.reset(writer);
+ }
+ }
+
+ void reset_writer() { writer_.reset(); }
+
+ ProofVerifier* proof_verifier() const;
+
+ void set_bind_to_address(QuicIpAddress address) {
+ bind_to_address_ = address;
+ }
+
+ QuicIpAddress bind_to_address() const { return bind_to_address_; }
+
+ void set_local_port(int local_port) { local_port_ = local_port; }
+
+ int local_port() const { return local_port_; }
+
+ const QuicSocketAddress& server_address() const { return server_address_; }
+
+ void set_server_address(const QuicSocketAddress& server_address) {
+ server_address_ = server_address;
+ }
+
+ QuicConnectionHelperInterface* helper() { return helper_.get(); }
+
+ NetworkHelper* network_helper();
+ const NetworkHelper* network_helper() const;
+
+ bool initialized() const { return initialized_; }
+
+ void SetPreSharedKey(QuicStringPiece key) {
+ crypto_config_.set_pre_shared_key(key);
+ }
+
+ protected:
+ // TODO(rch): Move GetNumSentClientHellosFromSession and
+ // GetNumReceivedServerConfigUpdatesFromSession into a new/better
+ // QuicSpdyClientSession class. The current inherits dependencies from
+ // Spdy. When that happens this class and all its subclasses should
+ // work with QuicSpdyClientSession instead of QuicSession.
+ // That will obviate the need for the pure virtual functions below.
+
+ // Extract the number of sent client hellos from the session.
+ virtual int GetNumSentClientHellosFromSession() = 0;
+
+ // The number of server config updates received. We assume no
+ // updates can be sent during a previously, statelessly rejected
+ // connection, so only the latest session is taken into account.
+ virtual int GetNumReceivedServerConfigUpdatesFromSession() = 0;
+
+ // If this client supports buffering data, resend it.
+ virtual void ResendSavedData() = 0;
+
+ // If this client supports buffering data, clear it.
+ virtual void ClearDataToResend() = 0;
+
+ // Takes ownership of |connection|. If you override this function,
+ // you probably want to call ResetSession() in your destructor.
+ // TODO(rch): Change the connection parameter to take in a
+ // std::unique_ptr<QuicConnection> instead.
+ virtual std::unique_ptr<QuicSession> CreateQuicClientSession(
+ const ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection) = 0;
+
+ // Generates the next ConnectionId for |server_id_|. By default, if the
+ // cached server config contains a server-designated ID, that ID will be
+ // returned. Otherwise, the next random ID will be returned.
+ QuicConnectionId GetNextConnectionId();
+
+ // Returns the next server-designated ConnectionId from the cached config for
+ // |server_id_|, if it exists. Otherwise, returns 0.
+ QuicConnectionId GetNextServerDesignatedConnectionId();
+
+ // Generates a new, random connection ID (as opposed to a server-designated
+ // connection ID).
+ virtual QuicConnectionId GenerateNewConnectionId();
+
+ QuicAlarmFactory* alarm_factory() { return alarm_factory_.get(); }
+
+ // Subclasses may need to explicitly clear the session on destruction
+ // if they create it with objects that will be destroyed before this is.
+ // You probably want to call this if you override CreateQuicSpdyClientSession.
+ void ResetSession() { session_.reset(); }
+
+ // Returns true if the corresponding of this client has active requests.
+ virtual bool HasActiveRequests() = 0;
+
+ private:
+ // Returns true and set |version| if client can reconnect with a different
+ // version.
+ bool CanReconnectWithDifferentVersion(ParsedQuicVersion* version) const;
+
+ // |server_id_| is a tuple (hostname, port, is_https) of the server.
+ QuicServerId server_id_;
+
+ // Tracks if the client is initialized to connect.
+ bool initialized_;
+
+ // Address of the server.
+ QuicSocketAddress server_address_;
+
+ // If initialized, the address to bind to.
+ QuicIpAddress bind_to_address_;
+
+ // Local port to bind to. Initialize to 0.
+ int local_port_;
+
+ // config_ and crypto_config_ contain configuration and cached state about
+ // servers.
+ QuicConfig config_;
+ QuicCryptoClientConfig crypto_config_;
+
+ // Helper to be used by created connections. Must outlive |session_|.
+ std::unique_ptr<QuicConnectionHelperInterface> helper_;
+
+ // Alarm factory to be used by created connections. Must outlive |session_|.
+ std::unique_ptr<QuicAlarmFactory> alarm_factory_;
+
+ // Writer used to actually send packets to the wire. Must outlive |session_|.
+ std::unique_ptr<QuicPacketWriter> writer_;
+
+ // Session which manages streams.
+ std::unique_ptr<QuicSession> session_;
+
+ // This vector contains QUIC versions which we currently support.
+ // This should be ordered such that the highest supported version is the first
+ // element, with subsequent elements in descending order (versions can be
+ // skipped as necessary). We will always pick supported_versions_[0] as the
+ // initial version to use.
+ ParsedQuicVersionVector supported_versions_;
+
+ // The initial value of maximum packet size of the connection. If set to
+ // zero, the default is used.
+ QuicByteCount initial_max_packet_length_;
+
+ // The number of stateless rejects received during the current/latest
+ // connection.
+ // TODO(jokulik): Consider some consistent naming scheme (or other) for member
+ // variables that are kept per-request, per-connection, and over the client's
+ // lifetime.
+ int num_stateless_rejects_received_;
+
+ // The number of hellos sent during the current/latest connection.
+ int num_sent_client_hellos_;
+
+ // Used to store any errors that occurred with the overall connection (as
+ // opposed to that associated with the last session object).
+ QuicErrorCode connection_error_;
+
+ // True when the client is attempting to connect or re-connect the session (in
+ // the case of a stateless reject). Set to false between a call to
+ // Disconnect() and the subsequent call to StartConnect(). When
+ // connected_or_attempting_connect_ is false, the session object corresponds
+ // to the previous client-level connection.
+ bool connected_or_attempting_connect_;
+
+ // The network helper used to create sockets and manage the event loop.
+ // Not owned by this class.
+ std::unique_ptr<NetworkHelper> network_helper_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_CLIENT_BASE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client_bin.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_client_bin.cc
new file mode 100644
index 00000000000..ee8262f751f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client_bin.cc
@@ -0,0 +1,403 @@
+// Copyright (c) 2012 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.
+
+// A binary wrapper for QuicClient.
+// Connects to a host using QUIC, sends a request to the provided URL, and
+// displays the response.
+//
+// Some usage examples:
+//
+// Standard request/response:
+// quic_client www.google.com
+// quic_client www.google.com --quiet
+// quic_client www.google.com --port=443
+//
+// Use a specific version:
+// quic_client www.google.com --quic_version=23
+//
+// Send a POST instead of a GET:
+// quic_client www.google.com --body="this is a POST body"
+//
+// Append additional headers to the request:
+// quic_client www.google.com --headers="Header-A: 1234; Header-B: 5678"
+//
+// Connect to a host different to the URL being requested:
+// quic_client mail.google.com --host=www.google.com
+//
+// Connect to a specific IP:
+// IP=`dig www.google.com +short | head -1`
+// quic_client www.google.com --host=${IP}
+//
+// Send repeated requests and change ephemeral port between requests
+// quic_client www.google.com --num_requests=10
+//
+// Try to connect to a host which does not speak QUIC:
+// quic_client www.example.com
+
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.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_ptr_util.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/quic_client.h"
+#include "net/third_party/quiche/src/quic/tools/quic_url.h"
+
+namespace {
+
+using quic::QuicSocketAddress;
+using quic::QuicStringPiece;
+using quic::QuicTextUtils;
+using quic::QuicUrl;
+
+class FakeProofVerifier : public quic::ProofVerifier {
+ public:
+ ~FakeProofVerifier() override {}
+ quic::QuicAsyncStatus VerifyProof(
+ const std::string& /*hostname*/,
+ const uint16_t /*port*/,
+ const std::string& /*server_config*/,
+ quic::QuicTransportVersion /*quic_version*/,
+ quic::QuicStringPiece /*chlo_hash*/,
+ const std::vector<std::string>& /*certs*/,
+ const std::string& /*cert_sct*/,
+ const std::string& /*signature*/,
+ const quic::ProofVerifyContext* /*context*/,
+ std::string* /*error_details*/,
+ std::unique_ptr<quic::ProofVerifyDetails>* /*details*/,
+ std::unique_ptr<quic::ProofVerifierCallback> /*callback*/) override {
+ return quic::QUIC_SUCCESS;
+ }
+ quic::QuicAsyncStatus VerifyCertChain(
+ const std::string& /*hostname*/,
+ const std::vector<std::string>& /*certs*/,
+ const quic::ProofVerifyContext* /*context*/,
+ std::string* /*error_details*/,
+ std::unique_ptr<quic::ProofVerifyDetails>* /*details*/,
+ std::unique_ptr<quic::ProofVerifierCallback> /*callback*/) override {
+ return quic::QUIC_SUCCESS;
+ }
+ std::unique_ptr<quic::ProofVerifyContext> CreateDefaultContext() override {
+ return nullptr;
+ }
+};
+
+QuicSocketAddress LookupAddress(std::string host, std::string port) {
+ addrinfo hint;
+ memset(&hint, 0, sizeof(hint));
+ hint.ai_protocol = IPPROTO_UDP;
+
+ addrinfo* info_list = nullptr;
+ int result = getaddrinfo(host.c_str(), port.c_str(), &hint, &info_list);
+ if (result != 0) {
+ QUIC_LOG(ERROR) << "Failed to look up " << host << ": "
+ << gai_strerror(result);
+ return QuicSocketAddress();
+ }
+
+ CHECK(info_list != nullptr);
+ std::unique_ptr<addrinfo, void (*)(addrinfo*)> info_list_owned(info_list,
+ freeaddrinfo);
+ return QuicSocketAddress(*info_list->ai_addr);
+}
+
+} // namespace
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+ std::string,
+ host,
+ "",
+ "The IP or hostname to connect to. If not provided, the host "
+ "will be derived from the provided URL.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t, port, 0, "The port to connect to.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(std::string,
+ body,
+ "",
+ "If set, send a POST with this body.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+ std::string,
+ body_hex,
+ "",
+ "If set, contents are converted from hex to ascii, before "
+ "sending as body of a POST. e.g. --body_hex=\"68656c6c6f\"");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+ std::string,
+ headers,
+ "",
+ "A semicolon separated list of key:value pairs to "
+ "add to request headers.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(bool,
+ quiet,
+ false,
+ "Set to true for a quieter output experience.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+ std::string,
+ quic_version,
+ "",
+ "QUIC version to speak, e.g. 21. If not set, then all available "
+ "versions are offered in the handshake. Also supports wire versions "
+ "such as Q043 or T099.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+ int32_t,
+ quic_ietf_draft,
+ 0,
+ "QUIC IETF draft number to use over the wire, e.g. 18. "
+ "By default this sets quic_version to T099. "
+ "This also enables required internal QUIC flags.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+ bool,
+ version_mismatch_ok,
+ false,
+ "If true, a version mismatch in the handshake is not considered a "
+ "failure. Useful for probing a server to determine if it speaks "
+ "any version of QUIC.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+ bool,
+ redirect_is_success,
+ true,
+ "If true, an HTTP response code of 3xx is considered to be a "
+ "successful response, otherwise a failure.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t,
+ initial_mtu,
+ 0,
+ "Initial MTU of the connection.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+ int32_t,
+ num_requests,
+ 1,
+ "How many sequential requests to make on a single connection.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(bool,
+ disable_certificate_verification,
+ false,
+ "If true, don't verify the server certificate.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+ bool,
+ drop_response_body,
+ false,
+ "If true, drop response body immediately after it is received.");
+
+int main(int argc, char* argv[]) {
+ QuicSystemEventLoop event_loop("quic_client");
+ const char* usage = "Usage: quic_client [options] <url>";
+
+ // All non-flag arguments should be interpreted as URLs to fetch.
+ std::vector<std::string> urls =
+ quic::QuicParseCommandLineFlags(usage, argc, argv);
+ if (urls.size() != 1) {
+ quic::QuicPrintCommandLineFlagHelp(usage);
+ exit(0);
+ }
+
+ QuicUrl url(urls[0], "https");
+ std::string host = GetQuicFlag(FLAGS_host);
+ if (host.empty()) {
+ host = url.host();
+ }
+ int port = GetQuicFlag(FLAGS_port);
+ if (port == 0) {
+ port = url.port();
+ }
+
+ // Determine IP address to connect to from supplied hostname.
+ QuicSocketAddress addr = LookupAddress(host, quic::QuicStrCat(port));
+ if (!addr.IsInitialized()) {
+ return 1;
+ }
+ std::cerr << "Resolved " << url.ToString() << " to " << addr.ToString()
+ << std::endl;
+
+ // Build the client, and try to connect.
+ quic::QuicEpollServer epoll_server;
+ quic::QuicServerId server_id(url.host(), port, false);
+ quic::ParsedQuicVersionVector versions = quic::CurrentSupportedVersions();
+
+ std::string quic_version_string = GetQuicFlag(FLAGS_quic_version);
+ const int32_t quic_ietf_draft = GetQuicFlag(FLAGS_quic_ietf_draft);
+ if (quic_ietf_draft > 0) {
+ quic::QuicVersionInitializeSupportForIetfDraft(quic_ietf_draft);
+ if (quic_version_string.length() == 0) {
+ quic_version_string = "T099";
+ }
+ }
+ if (quic_version_string.length() > 0) {
+ if (quic_version_string[0] == 'T') {
+ // ParseQuicVersionString checks quic_supports_tls_handshake.
+ SetQuicFlag(&FLAGS_quic_supports_tls_handshake, true);
+ }
+ quic::ParsedQuicVersion parsed_quic_version =
+ quic::ParseQuicVersionString(quic_version_string);
+ if (parsed_quic_version.transport_version ==
+ quic::QUIC_VERSION_UNSUPPORTED) {
+ return 1;
+ }
+ versions.clear();
+ versions.push_back(parsed_quic_version);
+ quic::QuicEnableVersion(parsed_quic_version);
+ }
+
+ const int32_t num_requests(GetQuicFlag(FLAGS_num_requests));
+ std::unique_ptr<quic::ProofVerifier> proof_verifier;
+ if (GetQuicFlag(FLAGS_disable_certificate_verification)) {
+ proof_verifier = quic::QuicMakeUnique<FakeProofVerifier>();
+ } else {
+ proof_verifier = quic::CreateDefaultProofVerifier();
+ }
+ quic::QuicClient client(addr, server_id, versions, &epoll_server,
+ std::move(proof_verifier));
+ int32_t initial_mtu = GetQuicFlag(FLAGS_initial_mtu);
+ client.set_initial_max_packet_length(
+ initial_mtu != 0 ? initial_mtu : quic::kDefaultMaxPacketSize);
+ client.set_drop_response_body(GetQuicFlag(FLAGS_drop_response_body));
+ if (!client.Initialize()) {
+ std::cerr << "Failed to initialize client." << std::endl;
+ return 1;
+ }
+ if (!client.Connect()) {
+ quic::QuicErrorCode error = client.session()->error();
+ if (error == quic::QUIC_INVALID_VERSION) {
+ std::cerr << "Server talks QUIC, but none of the versions supported by "
+ << "this client: " << ParsedQuicVersionVectorToString(versions)
+ << std::endl;
+ // 0: No error.
+ // 20: Failed to connect due to QUIC_INVALID_VERSION.
+ return GetQuicFlag(FLAGS_version_mismatch_ok) ? 0 : 20;
+ }
+ std::cerr << "Failed to connect to " << addr.ToString()
+ << ". Error: " << quic::QuicErrorCodeToString(error) << std::endl;
+ return 1;
+ }
+ std::cerr << "Connected to " << addr.ToString() << std::endl;
+
+ // Construct the string body from flags, if provided.
+ std::string body = GetQuicFlag(FLAGS_body);
+ if (!GetQuicFlag(FLAGS_body_hex).empty()) {
+ DCHECK(GetQuicFlag(FLAGS_body).empty())
+ << "Only set one of --body and --body_hex.";
+ body = QuicTextUtils::HexDecode(GetQuicFlag(FLAGS_body_hex));
+ }
+
+ // Construct a GET or POST request for supplied URL.
+ spdy::SpdyHeaderBlock header_block;
+ header_block[":method"] = body.empty() ? "GET" : "POST";
+ header_block[":scheme"] = url.scheme();
+ header_block[":authority"] = url.HostPort();
+ header_block[":path"] = url.PathParamsQuery();
+
+ // Append any additional headers supplied on the command line.
+ for (QuicStringPiece sp :
+ QuicTextUtils::Split(GetQuicFlag(FLAGS_headers), ';')) {
+ QuicTextUtils::RemoveLeadingAndTrailingWhitespace(&sp);
+ if (sp.empty()) {
+ continue;
+ }
+ std::vector<QuicStringPiece> kv = QuicTextUtils::Split(sp, ':');
+ QuicTextUtils::RemoveLeadingAndTrailingWhitespace(&kv[0]);
+ QuicTextUtils::RemoveLeadingAndTrailingWhitespace(&kv[1]);
+ header_block[kv[0]] = kv[1];
+ }
+
+ // Make sure to store the response, for later output.
+ client.set_store_response(true);
+
+ for (int i = 0; i < num_requests; ++i) {
+ // Send the request.
+ client.SendRequestAndWaitForResponse(header_block, body, /*fin=*/true);
+
+ // Print request and response details.
+ if (!GetQuicFlag(FLAGS_quiet)) {
+ std::cout << "Request:" << std::endl;
+ std::cout << "headers:" << header_block.DebugString();
+ if (!GetQuicFlag(FLAGS_body_hex).empty()) {
+ // Print the user provided hex, rather than binary body.
+ std::cout << "body:\n"
+ << QuicTextUtils::HexDump(
+ QuicTextUtils::HexDecode(GetQuicFlag(FLAGS_body_hex)))
+ << std::endl;
+ } else {
+ std::cout << "body: " << body << std::endl;
+ }
+ std::cout << std::endl;
+
+ if (!client.preliminary_response_headers().empty()) {
+ std::cout << "Preliminary response headers: "
+ << client.preliminary_response_headers() << std::endl;
+ std::cout << std::endl;
+ }
+
+ std::cout << "Response:" << std::endl;
+ std::cout << "headers: " << client.latest_response_headers() << std::endl;
+ std::string response_body = client.latest_response_body();
+ if (!GetQuicFlag(FLAGS_body_hex).empty()) {
+ // Assume response is binary data.
+ std::cout << "body:\n"
+ << QuicTextUtils::HexDump(response_body) << std::endl;
+ } else {
+ std::cout << "body: " << response_body << std::endl;
+ }
+ std::cout << "trailers: " << client.latest_response_trailers()
+ << std::endl;
+ }
+
+ if (!client.connected()) {
+ std::cerr << "Request caused connection failure. Error: "
+ << quic::QuicErrorCodeToString(client.session()->error())
+ << std::endl;
+ return 1;
+ }
+
+ size_t response_code = client.latest_response_code();
+ if (response_code >= 200 && response_code < 300) {
+ std::cerr << "Request succeeded (" << response_code << ")." << std::endl;
+ } else if (response_code >= 300 && response_code < 400) {
+ if (GetQuicFlag(FLAGS_redirect_is_success)) {
+ std::cerr << "Request succeeded (redirect " << response_code << ")."
+ << std::endl;
+ } else {
+ std::cerr << "Request failed (redirect " << response_code << ")."
+ << std::endl;
+ return 1;
+ }
+ } else {
+ std::cerr << "Request failed (" << response_code << ")." << std::endl;
+ return 1;
+ }
+
+ // Change the ephemeral port if there are more requests to do.
+ if (i + 1 < num_requests) {
+ if (!client.ChangeEphemeralPort()) {
+ std::cerr << "Failed to change ephemeral port." << std::endl;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client_epoll_network_helper.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_client_epoll_network_helper.cc
new file mode 100644
index 00000000000..e545ee2c62c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client_epoll_network_helper.cc
@@ -0,0 +1,206 @@
+// Copyright (c) 2012 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/tools/quic_client_epoll_network_helper.h"
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
+#include "net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_system_event_loop.h"
+#include "net/quic/platform/impl/quic_socket_utils.h"
+
+#ifndef SO_RXQ_OVFL
+#define SO_RXQ_OVFL 40
+#endif
+
+namespace quic {
+
+namespace {
+const int kEpollFlags = EPOLLIN | EPOLLOUT | EPOLLET;
+} // namespace
+
+QuicClientEpollNetworkHelper::QuicClientEpollNetworkHelper(
+ QuicEpollServer* epoll_server,
+ QuicClientBase* client)
+ : epoll_server_(epoll_server),
+ packets_dropped_(0),
+ overflow_supported_(false),
+ packet_reader_(new QuicPacketReader()),
+ client_(client),
+ max_reads_per_epoll_loop_(std::numeric_limits<int>::max()) {}
+
+QuicClientEpollNetworkHelper::~QuicClientEpollNetworkHelper() {
+ if (client_->connected()) {
+ client_->session()->connection()->CloseConnection(
+ QUIC_PEER_GOING_AWAY, "Client being torn down",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ }
+
+ CleanUpAllUDPSockets();
+}
+
+std::string QuicClientEpollNetworkHelper::Name() const {
+ return "QuicClientEpollNetworkHelper";
+}
+
+bool QuicClientEpollNetworkHelper::CreateUDPSocketAndBind(
+ QuicSocketAddress server_address,
+ QuicIpAddress bind_to_address,
+ int bind_to_port) {
+ epoll_server_->set_timeout_in_us(50 * 1000);
+
+ int fd = CreateUDPSocket(server_address, &overflow_supported_);
+ if (fd < 0) {
+ return false;
+ }
+
+ QuicSocketAddress client_address;
+ if (bind_to_address.IsInitialized()) {
+ client_address = QuicSocketAddress(bind_to_address, client_->local_port());
+ } else if (server_address.host().address_family() == IpAddressFamily::IP_V4) {
+ client_address = QuicSocketAddress(QuicIpAddress::Any4(), bind_to_port);
+ } else {
+ client_address = QuicSocketAddress(QuicIpAddress::Any6(), bind_to_port);
+ }
+
+ sockaddr_storage addr = client_address.generic_address();
+ int rc = bind(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
+ if (rc < 0) {
+ QUIC_LOG(ERROR) << "Bind failed: " << strerror(errno);
+ return false;
+ }
+
+ if (client_address.FromSocket(fd) != 0) {
+ QUIC_LOG(ERROR) << "Unable to get self address. Error: "
+ << strerror(errno);
+ }
+
+ fd_address_map_[fd] = client_address;
+
+ epoll_server_->RegisterFD(fd, this, kEpollFlags);
+ return true;
+}
+
+void QuicClientEpollNetworkHelper::CleanUpUDPSocket(int fd) {
+ CleanUpUDPSocketImpl(fd);
+ fd_address_map_.erase(fd);
+}
+
+void QuicClientEpollNetworkHelper::CleanUpAllUDPSockets() {
+ for (std::pair<int, QuicSocketAddress> fd_address : fd_address_map_) {
+ CleanUpUDPSocketImpl(fd_address.first);
+ }
+ fd_address_map_.clear();
+}
+
+void QuicClientEpollNetworkHelper::CleanUpUDPSocketImpl(int fd) {
+ if (fd > -1) {
+ epoll_server_->UnregisterFD(fd);
+ int rc = close(fd);
+ DCHECK_EQ(0, rc);
+ }
+}
+
+void QuicClientEpollNetworkHelper::RunEventLoop() {
+ QuicRunSystemEventLoopIteration();
+ epoll_server_->WaitForEventsAndExecuteCallbacks();
+}
+
+void QuicClientEpollNetworkHelper::OnRegistration(QuicEpollServer* eps,
+ int fd,
+ int event_mask) {}
+void QuicClientEpollNetworkHelper::OnModification(int fd, int event_mask) {}
+void QuicClientEpollNetworkHelper::OnUnregistration(int fd, bool replaced) {}
+void QuicClientEpollNetworkHelper::OnShutdown(QuicEpollServer* eps, int fd) {}
+
+void QuicClientEpollNetworkHelper::OnEvent(int fd, QuicEpollEvent* event) {
+ DCHECK_EQ(fd, GetLatestFD());
+
+ if (event->in_events & EPOLLIN) {
+ DVLOG(1) << "Read packets on EPOLLIN";
+ int times_to_read = max_reads_per_epoll_loop_;
+ bool more_to_read = true;
+ QuicPacketCount packets_dropped = 0;
+ while (client_->connected() && more_to_read && times_to_read > 0) {
+ more_to_read = packet_reader_->ReadAndDispatchPackets(
+ GetLatestFD(), GetLatestClientAddress().port(),
+ *client_->helper()->GetClock(), this,
+ overflow_supported_ ? &packets_dropped : nullptr);
+ --times_to_read;
+ }
+ if (packets_dropped_ < packets_dropped) {
+ QUIC_LOG(ERROR)
+ << packets_dropped - packets_dropped_
+ << " more packets are dropped in the socket receive buffer.";
+ packets_dropped_ = packets_dropped;
+ }
+ if (client_->connected() && more_to_read) {
+ event->out_ready_mask |= EPOLLIN;
+ }
+ }
+ if (client_->connected() && (event->in_events & EPOLLOUT)) {
+ client_->writer()->SetWritable();
+ client_->session()->connection()->OnCanWrite();
+ }
+ if (event->in_events & EPOLLERR) {
+ QUIC_DLOG(INFO) << "Epollerr";
+ }
+}
+
+QuicPacketWriter* QuicClientEpollNetworkHelper::CreateQuicPacketWriter() {
+ return new QuicDefaultPacketWriter(GetLatestFD());
+}
+
+void QuicClientEpollNetworkHelper::SetClientPort(int port) {
+ fd_address_map_.back().second =
+ QuicSocketAddress(GetLatestClientAddress().host(), port);
+}
+
+QuicSocketAddress QuicClientEpollNetworkHelper::GetLatestClientAddress() const {
+ if (fd_address_map_.empty()) {
+ return QuicSocketAddress();
+ }
+
+ return fd_address_map_.back().second;
+}
+
+int QuicClientEpollNetworkHelper::GetLatestFD() const {
+ if (fd_address_map_.empty()) {
+ return -1;
+ }
+
+ return fd_address_map_.back().first;
+}
+
+void QuicClientEpollNetworkHelper::ProcessPacket(
+ const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ const QuicReceivedPacket& packet) {
+ client_->session()->ProcessUdpPacket(self_address, peer_address, packet);
+}
+
+int QuicClientEpollNetworkHelper::CreateUDPSocket(
+ QuicSocketAddress server_address,
+ bool* overflow_supported) {
+ return QuicSocketUtils::CreateUDPSocket(
+ server_address,
+ /*receive_buffer_size =*/kDefaultSocketReceiveBuffer,
+ /*send_buffer_size =*/kDefaultSocketReceiveBuffer, overflow_supported);
+}
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client_epoll_network_helper.h b/chromium/net/third_party/quiche/src/quic/tools/quic_client_epoll_network_helper.h
new file mode 100644
index 00000000000..89d12946c7b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client_epoll_network_helper.h
@@ -0,0 +1,135 @@
+// Copyright (c) 2012 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.
+
+// An implementation of the QuicClientBase::NetworkHelper
+// that is based off the epoll server.
+
+#ifndef QUICHE_QUIC_TOOLS_QUIC_CLIENT_EPOLL_NETWORK_HELPER_H_
+#define QUICHE_QUIC_TOOLS_QUIC_CLIENT_EPOLL_NETWORK_HELPER_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_reader.h"
+#include "net/third_party/quiche/src/quic/core/quic_process_packet_interface.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h"
+#include "net/third_party/quiche/src/quic/tools/quic_client_base.h"
+
+namespace quic {
+
+namespace test {
+class QuicClientPeer;
+} // namespace test
+
+// An implementation of the QuicClientBase::NetworkHelper based off
+// the epoll server.
+class QuicClientEpollNetworkHelper : public QuicClientBase::NetworkHelper,
+ public QuicEpollCallbackInterface,
+ public ProcessPacketInterface {
+ public:
+ // Create a quic client, which will have events managed by an externally owned
+ // EpollServer.
+ QuicClientEpollNetworkHelper(QuicEpollServer* epoll_server,
+ QuicClientBase* client);
+ QuicClientEpollNetworkHelper(const QuicClientEpollNetworkHelper&) = delete;
+ QuicClientEpollNetworkHelper& operator=(const QuicClientEpollNetworkHelper&) =
+ delete;
+
+ ~QuicClientEpollNetworkHelper() override;
+
+ // Return a name describing the class for use in debug/error reporting.
+ std::string Name() const override;
+
+ // From EpollCallbackInterface
+ void OnRegistration(QuicEpollServer* eps, int fd, int event_mask) override;
+ void OnModification(int fd, int event_mask) override;
+ void OnEvent(int fd, QuicEpollEvent* event) override;
+ // |fd_| can be unregistered without the client being disconnected. This
+ // happens in b3m QuicProber where we unregister |fd_| to feed in events to
+ // the client from the SelectServer.
+ void OnUnregistration(int fd, bool replaced) override;
+ void OnShutdown(QuicEpollServer* eps, int fd) override;
+
+ // From ProcessPacketInterface. This will be called for each received
+ // packet.
+ void ProcessPacket(const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ const QuicReceivedPacket& packet) override;
+
+ // From NetworkHelper.
+ void RunEventLoop() override;
+ bool CreateUDPSocketAndBind(QuicSocketAddress server_address,
+ QuicIpAddress bind_to_address,
+ int bind_to_port) override;
+ void CleanUpAllUDPSockets() override;
+ QuicSocketAddress GetLatestClientAddress() const override;
+ QuicPacketWriter* CreateQuicPacketWriter() override;
+
+ // Accessors provided for convenience, not part of any interface.
+
+ QuicEpollServer* epoll_server() { return epoll_server_; }
+
+ const QuicLinkedHashMap<int, QuicSocketAddress>& fd_address_map() const {
+ return fd_address_map_;
+ }
+
+ // If the client has at least one UDP socket, return the latest created one.
+ // Otherwise, return -1.
+ int GetLatestFD() const;
+
+ // Create socket for connection to |server_address| with default socket
+ // options.
+ // Return fd index.
+ virtual int CreateUDPSocket(QuicSocketAddress server_address,
+ bool* overflow_supported);
+
+ QuicClientBase* client() { return client_; }
+
+ void set_max_reads_per_epoll_loop(int num_reads) {
+ max_reads_per_epoll_loop_ = num_reads;
+ }
+ // If |fd| is an open UDP socket, unregister and close it. Otherwise, do
+ // nothing.
+ void CleanUpUDPSocket(int fd);
+
+ private:
+ friend class test::QuicClientPeer;
+
+ // Used for testing.
+ void SetClientPort(int port);
+
+ // Actually clean up |fd|.
+ void CleanUpUDPSocketImpl(int fd);
+
+ // Listens for events on the client socket.
+ QuicEpollServer* epoll_server_;
+
+ // Map mapping created UDP sockets to their addresses. By using linked hash
+ // map, the order of socket creation can be recorded.
+ QuicLinkedHashMap<int, QuicSocketAddress> fd_address_map_;
+
+ // If overflow_supported_ is true, this will be the number of packets dropped
+ // during the lifetime of the server.
+ QuicPacketCount packets_dropped_;
+
+ // True if the kernel supports SO_RXQ_OVFL, the number of packets dropped
+ // because the socket would otherwise overflow.
+ bool overflow_supported_;
+
+ // Point to a QuicPacketReader object on the heap. The reader allocates more
+ // space than allowed on the stack.
+ std::unique_ptr<QuicPacketReader> packet_reader_;
+
+ QuicClientBase* client_;
+
+ int max_reads_per_epoll_loop_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_CLIENT_EPOLL_NETWORK_HELPER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_client_test.cc
new file mode 100644
index 00000000000..12a687c1671
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client_test.cc
@@ -0,0 +1,120 @@
+// Copyright (c) 2014 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/tools/quic_client.h"
+
+#include <dirent.h>
+#include <sys/types.h>
+
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_port_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test_loopback.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_client_peer.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+const char* kPathToFds = "/proc/self/fd";
+
+std::string ReadLink(const std::string& path) {
+ std::string result(PATH_MAX, '\0');
+ ssize_t result_size = readlink(path.c_str(), &result[0], result.size());
+ CHECK(result_size > 0 && static_cast<size_t>(result_size) < result.size());
+ result.resize(result_size);
+ return result;
+}
+
+// Counts the number of open sockets for the current process.
+size_t NumOpenSocketFDs() {
+ size_t socket_count = 0;
+ dirent* file;
+ std::unique_ptr<DIR, int (*)(DIR*)> fd_directory(opendir(kPathToFds),
+ closedir);
+ while ((file = readdir(fd_directory.get())) != nullptr) {
+ QuicStringPiece name(file->d_name);
+ if (name == "." || name == "..") {
+ continue;
+ }
+
+ std::string fd_path = ReadLink(QuicStrCat(kPathToFds, "/", name));
+ if (QuicTextUtils::StartsWith(fd_path, "socket:")) {
+ socket_count++;
+ }
+ }
+ return socket_count;
+}
+
+// Creates a new QuicClient and Initializes it. Caller is responsible for
+// deletion.
+std::unique_ptr<QuicClient> CreateAndInitializeQuicClient(QuicEpollServer* eps,
+ uint16_t port) {
+ QuicSocketAddress server_address(QuicSocketAddress(TestLoopback(), port));
+ QuicServerId server_id("hostname", server_address.port(), false);
+ ParsedQuicVersionVector versions = AllSupportedVersions();
+ auto client =
+ QuicMakeUnique<QuicClient>(server_address, server_id, versions, eps,
+ crypto_test_utils::ProofVerifierForTesting());
+ EXPECT_TRUE(client->Initialize());
+ return client;
+}
+
+class QuicClientTest : public QuicTest {};
+
+TEST_F(QuicClientTest, DoNotLeakSocketFDs) {
+ // Make sure that the QuicClient doesn't leak socket FDs. Doing so could cause
+ // port exhaustion in long running processes which repeatedly create clients.
+
+ // Record initial number of FDs, after creating EpollServer and creating and
+ // destroying a single client (the latter is needed since initializing
+ // platform dependencies like certificate verifier may open a persistent
+ // socket).
+ QuicEpollServer eps;
+ CreateAndInitializeQuicClient(&eps, QuicPickUnusedPortOrDie());
+ size_t number_of_open_fds = NumOpenSocketFDs();
+
+ // Create a number of clients, initialize them, and verify this has resulted
+ // in additional FDs being opened.
+ const int kNumClients = 50;
+ for (int i = 0; i < kNumClients; ++i) {
+ std::unique_ptr<QuicClient> client(
+ CreateAndInitializeQuicClient(&eps, QuicPickUnusedPortOrDie()));
+
+ // Initializing the client will create a new FD.
+ EXPECT_LT(number_of_open_fds, NumOpenSocketFDs());
+ }
+
+ // The FDs created by the QuicClients should now be closed.
+ EXPECT_EQ(number_of_open_fds, NumOpenSocketFDs());
+}
+
+TEST_F(QuicClientTest, CreateAndCleanUpUDPSockets) {
+ QuicEpollServer eps;
+ size_t number_of_open_fds = NumOpenSocketFDs();
+
+ std::unique_ptr<QuicClient> client(
+ CreateAndInitializeQuicClient(&eps, QuicPickUnusedPortOrDie()));
+ EXPECT_EQ(number_of_open_fds + 1, NumOpenSocketFDs());
+ // Create more UDP sockets.
+ EXPECT_TRUE(QuicClientPeer::CreateUDPSocketAndBind(client.get()));
+ EXPECT_EQ(number_of_open_fds + 2, NumOpenSocketFDs());
+ EXPECT_TRUE(QuicClientPeer::CreateUDPSocketAndBind(client.get()));
+ EXPECT_EQ(number_of_open_fds + 3, NumOpenSocketFDs());
+
+ // Clean up UDP sockets.
+ QuicClientPeer::CleanUpUDPSocket(client.get(), client->GetLatestFD());
+ EXPECT_EQ(number_of_open_fds + 2, NumOpenSocketFDs());
+ QuicClientPeer::CleanUpUDPSocket(client.get(), client->GetLatestFD());
+ EXPECT_EQ(number_of_open_fds + 1, NumOpenSocketFDs());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.cc
new file mode 100644
index 00000000000..76a52c3bf87
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.cc
@@ -0,0 +1,419 @@
+// Copyright (c) 2012 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/tools/quic_memory_cache_backend.h"
+
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_file_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using spdy::kV3LowestPriority;
+using spdy::SpdyHeaderBlock;
+
+namespace quic {
+
+QuicMemoryCacheBackend::ResourceFile::ResourceFile(const std::string& file_name)
+ : file_name_(file_name) {}
+
+QuicMemoryCacheBackend::ResourceFile::~ResourceFile() = default;
+
+void QuicMemoryCacheBackend::ResourceFile::Read() {
+ ReadFileContents(file_name_, &file_contents_);
+
+ // First read the headers.
+ size_t start = 0;
+ while (start < file_contents_.length()) {
+ size_t pos = file_contents_.find("\n", start);
+ if (pos == std::string::npos) {
+ QUIC_LOG(DFATAL) << "Headers invalid or empty, ignoring: " << file_name_;
+ return;
+ }
+ size_t len = pos - start;
+ // Support both dos and unix line endings for convenience.
+ if (file_contents_[pos - 1] == '\r') {
+ len -= 1;
+ }
+ QuicStringPiece line(file_contents_.data() + start, len);
+ start = pos + 1;
+ // Headers end with an empty line.
+ if (line.empty()) {
+ break;
+ }
+ // Extract the status from the HTTP first line.
+ if (line.substr(0, 4) == "HTTP") {
+ pos = line.find(" ");
+ if (pos == std::string::npos) {
+ QUIC_LOG(DFATAL) << "Headers invalid or empty, ignoring: "
+ << file_name_;
+ return;
+ }
+ spdy_headers_[":status"] = line.substr(pos + 1, 3);
+ continue;
+ }
+ // Headers are "key: value".
+ pos = line.find(": ");
+ if (pos == std::string::npos) {
+ QUIC_LOG(DFATAL) << "Headers invalid or empty, ignoring: " << file_name_;
+ return;
+ }
+ spdy_headers_.AppendValueOrAddHeader(
+ QuicTextUtils::ToLower(line.substr(0, pos)), line.substr(pos + 2));
+ }
+
+ // The connection header is prohibited in HTTP/2.
+ spdy_headers_.erase("connection");
+
+ // Override the URL with the X-Original-Url header, if present.
+ auto it = spdy_headers_.find("x-original-url");
+ if (it != spdy_headers_.end()) {
+ x_original_url_ = it->second;
+ HandleXOriginalUrl();
+ }
+
+ // X-Push-URL header is a relatively quick way to support sever push
+ // in the toy server. A production server should use link=preload
+ // stuff as described in https://w3c.github.io/preload/.
+ it = spdy_headers_.find("x-push-url");
+ if (it != spdy_headers_.end()) {
+ QuicStringPiece push_urls = it->second;
+ size_t start = 0;
+ while (start < push_urls.length()) {
+ size_t pos = push_urls.find('\0', start);
+ if (pos == std::string::npos) {
+ push_urls_.push_back(QuicStringPiece(push_urls.data() + start,
+ push_urls.length() - start));
+ break;
+ }
+ push_urls_.push_back(QuicStringPiece(push_urls.data() + start, pos));
+ start += pos + 1;
+ }
+ }
+
+ body_ = QuicStringPiece(file_contents_.data() + start,
+ file_contents_.size() - start);
+}
+
+void QuicMemoryCacheBackend::ResourceFile::SetHostPathFromBase(
+ QuicStringPiece base) {
+ size_t path_start = base.find_first_of('/');
+ DCHECK_LT(0UL, path_start);
+ host_ = base.substr(0, path_start);
+ size_t query_start = base.find_first_of(',');
+ if (query_start > 0) {
+ path_ = base.substr(path_start, query_start - 1);
+ } else {
+ path_ = base.substr(path_start);
+ }
+}
+
+QuicStringPiece QuicMemoryCacheBackend::ResourceFile::RemoveScheme(
+ QuicStringPiece url) {
+ if (QuicTextUtils::StartsWith(url, "https://")) {
+ url.remove_prefix(8);
+ } else if (QuicTextUtils::StartsWith(url, "http://")) {
+ url.remove_prefix(7);
+ }
+ return url;
+}
+
+void QuicMemoryCacheBackend::ResourceFile::HandleXOriginalUrl() {
+ QuicStringPiece url(x_original_url_);
+ // Remove the protocol so we can add it below.
+ url = RemoveScheme(url);
+ SetHostPathFromBase(url);
+}
+
+const QuicBackendResponse* QuicMemoryCacheBackend::GetResponse(
+ QuicStringPiece host,
+ QuicStringPiece path) const {
+ QuicWriterMutexLock lock(&response_mutex_);
+
+ auto it = responses_.find(GetKey(host, path));
+ if (it == responses_.end()) {
+ DVLOG(1) << "Get response for resource failed: host " << host << " path "
+ << path;
+ if (default_response_) {
+ return default_response_.get();
+ }
+ return nullptr;
+ }
+ return it->second.get();
+}
+
+typedef QuicBackendResponse::ServerPushInfo ServerPushInfo;
+typedef QuicBackendResponse::SpecialResponseType SpecialResponseType;
+
+void QuicMemoryCacheBackend::AddSimpleResponse(QuicStringPiece host,
+ QuicStringPiece path,
+ int response_code,
+ QuicStringPiece body) {
+ SpdyHeaderBlock response_headers;
+ response_headers[":status"] = QuicTextUtils::Uint64ToString(response_code);
+ response_headers["content-length"] =
+ QuicTextUtils::Uint64ToString(body.length());
+ AddResponse(host, path, std::move(response_headers), body);
+}
+
+void QuicMemoryCacheBackend::AddSimpleResponseWithServerPushResources(
+ QuicStringPiece host,
+ QuicStringPiece path,
+ int response_code,
+ QuicStringPiece body,
+ std::list<ServerPushInfo> push_resources) {
+ AddSimpleResponse(host, path, response_code, body);
+ MaybeAddServerPushResources(host, path, push_resources);
+}
+
+void QuicMemoryCacheBackend::AddDefaultResponse(QuicBackendResponse* response) {
+ QuicWriterMutexLock lock(&response_mutex_);
+ default_response_.reset(response);
+}
+
+void QuicMemoryCacheBackend::AddResponse(QuicStringPiece host,
+ QuicStringPiece path,
+ SpdyHeaderBlock response_headers,
+ QuicStringPiece response_body) {
+ AddResponseImpl(host, path, QuicBackendResponse::REGULAR_RESPONSE,
+ std::move(response_headers), response_body, SpdyHeaderBlock(),
+ 0);
+}
+
+void QuicMemoryCacheBackend::AddResponse(QuicStringPiece host,
+ QuicStringPiece path,
+ SpdyHeaderBlock response_headers,
+ QuicStringPiece response_body,
+ SpdyHeaderBlock response_trailers) {
+ AddResponseImpl(host, path, QuicBackendResponse::REGULAR_RESPONSE,
+ std::move(response_headers), response_body,
+ std::move(response_trailers), 0);
+}
+
+void QuicMemoryCacheBackend::AddSpecialResponse(
+ QuicStringPiece host,
+ QuicStringPiece path,
+ SpecialResponseType response_type) {
+ AddResponseImpl(host, path, response_type, SpdyHeaderBlock(), "",
+ SpdyHeaderBlock(), 0);
+}
+
+void QuicMemoryCacheBackend::AddSpecialResponse(
+ QuicStringPiece host,
+ QuicStringPiece path,
+ spdy::SpdyHeaderBlock response_headers,
+ QuicStringPiece response_body,
+ SpecialResponseType response_type) {
+ AddResponseImpl(host, path, response_type, std::move(response_headers),
+ response_body, SpdyHeaderBlock(), 0);
+}
+
+void QuicMemoryCacheBackend::AddStopSendingResponse(
+ QuicStringPiece host,
+ QuicStringPiece path,
+ spdy::SpdyHeaderBlock response_headers,
+ QuicStringPiece response_body,
+ uint16_t stop_sending_code) {
+ AddResponseImpl(host, path, SpecialResponseType::STOP_SENDING,
+ std::move(response_headers), response_body, SpdyHeaderBlock(),
+ stop_sending_code);
+}
+
+QuicMemoryCacheBackend::QuicMemoryCacheBackend() : cache_initialized_(false) {}
+
+bool QuicMemoryCacheBackend::InitializeBackend(
+ const std::string& cache_directory) {
+ if (cache_directory.empty()) {
+ QUIC_BUG << "cache_directory must not be empty.";
+ return false;
+ }
+ QUIC_LOG(INFO)
+ << "Attempting to initialize QuicMemoryCacheBackend from directory: "
+ << cache_directory;
+ std::vector<std::string> files = ReadFileContents(cache_directory);
+ std::list<std::unique_ptr<ResourceFile>> resource_files;
+ for (const auto& filename : files) {
+ std::unique_ptr<ResourceFile> resource_file(new ResourceFile(filename));
+
+ // Tease apart filename into host and path.
+ QuicStringPiece base(resource_file->file_name());
+ base.remove_prefix(cache_directory.length());
+ if (base[0] == '/') {
+ base.remove_prefix(1);
+ }
+
+ resource_file->SetHostPathFromBase(base);
+ resource_file->Read();
+
+ AddResponse(resource_file->host(), resource_file->path(),
+ resource_file->spdy_headers().Clone(), resource_file->body());
+
+ resource_files.push_back(std::move(resource_file));
+ }
+
+ for (const auto& resource_file : resource_files) {
+ std::list<ServerPushInfo> push_resources;
+ for (const auto& push_url : resource_file->push_urls()) {
+ QuicUrl url(push_url);
+ const QuicBackendResponse* response = GetResponse(url.host(), url.path());
+ if (!response) {
+ QUIC_BUG << "Push URL '" << push_url << "' not found.";
+ return false;
+ }
+ push_resources.push_back(ServerPushInfo(url, response->headers().Clone(),
+ kV3LowestPriority,
+ (std::string(response->body()))));
+ }
+ MaybeAddServerPushResources(resource_file->host(), resource_file->path(),
+ push_resources);
+ }
+ cache_initialized_ = true;
+ return true;
+}
+
+bool QuicMemoryCacheBackend::IsBackendInitialized() const {
+ return cache_initialized_;
+}
+
+void QuicMemoryCacheBackend::FetchResponseFromBackend(
+ const SpdyHeaderBlock& request_headers,
+ const std::string& request_body,
+ QuicSimpleServerBackend::RequestHandler* quic_stream) {
+ const QuicBackendResponse* quic_response = nullptr;
+ // Find response in cache. If not found, send error response.
+ auto authority = request_headers.find(":authority");
+ auto path = request_headers.find(":path");
+ if (authority != request_headers.end() && path != request_headers.end()) {
+ quic_response = GetResponse(authority->second, path->second);
+ }
+
+ std::string request_url =
+ std::string(authority->second) + std::string(path->second);
+ std::list<ServerPushInfo> resources = GetServerPushResources(request_url);
+ QUIC_DVLOG(1)
+ << "Fetching QUIC response from backend in-memory cache for url "
+ << request_url;
+ quic_stream->OnResponseBackendComplete(quic_response, resources);
+}
+
+// The memory cache does not have a per-stream handler
+void QuicMemoryCacheBackend::CloseBackendResponseStream(
+ QuicSimpleServerBackend::RequestHandler* quic_stream) {}
+
+std::list<ServerPushInfo> QuicMemoryCacheBackend::GetServerPushResources(
+ std::string request_url) {
+ QuicWriterMutexLock lock(&response_mutex_);
+
+ std::list<ServerPushInfo> resources;
+ auto resource_range = server_push_resources_.equal_range(request_url);
+ for (auto it = resource_range.first; it != resource_range.second; ++it) {
+ resources.push_back(it->second);
+ }
+ QUIC_DVLOG(1) << "Found " << resources.size() << " push resources for "
+ << request_url;
+ return resources;
+}
+
+QuicMemoryCacheBackend::~QuicMemoryCacheBackend() {
+ {
+ QuicWriterMutexLock lock(&response_mutex_);
+ responses_.clear();
+ }
+}
+
+void QuicMemoryCacheBackend::AddResponseImpl(QuicStringPiece host,
+ QuicStringPiece path,
+ SpecialResponseType response_type,
+ SpdyHeaderBlock response_headers,
+ QuicStringPiece response_body,
+ SpdyHeaderBlock response_trailers,
+ uint16_t stop_sending_code) {
+ QuicWriterMutexLock lock(&response_mutex_);
+
+ DCHECK(!host.empty()) << "Host must be populated, e.g. \"www.google.com\"";
+ std::string key = GetKey(host, path);
+ if (QuicContainsKey(responses_, key)) {
+ QUIC_BUG << "Response for '" << key << "' already exists!";
+ return;
+ }
+ auto new_response = QuicMakeUnique<QuicBackendResponse>();
+ new_response->set_response_type(response_type);
+ new_response->set_headers(std::move(response_headers));
+ new_response->set_body(response_body);
+ new_response->set_trailers(std::move(response_trailers));
+ new_response->set_stop_sending_code(stop_sending_code);
+ QUIC_DVLOG(1) << "Add response with key " << key;
+ responses_[key] = std::move(new_response);
+}
+
+std::string QuicMemoryCacheBackend::GetKey(QuicStringPiece host,
+ QuicStringPiece path) const {
+ std::string host_string = std::string(host);
+ size_t port = host_string.find(':');
+ if (port != std::string::npos)
+ host_string = std::string(host_string.c_str(), port);
+ return host_string + std::string(path);
+}
+
+void QuicMemoryCacheBackend::MaybeAddServerPushResources(
+ QuicStringPiece request_host,
+ QuicStringPiece request_path,
+ std::list<ServerPushInfo> push_resources) {
+ std::string request_url = GetKey(request_host, request_path);
+
+ for (const auto& push_resource : push_resources) {
+ if (PushResourceExistsInCache(request_url, push_resource)) {
+ continue;
+ }
+
+ QUIC_DVLOG(1) << "Add request-resource association: request url "
+ << request_url << " push url "
+ << push_resource.request_url.ToString()
+ << " response headers "
+ << push_resource.headers.DebugString();
+ {
+ QuicWriterMutexLock lock(&response_mutex_);
+ server_push_resources_.insert(std::make_pair(request_url, push_resource));
+ }
+ std::string host = push_resource.request_url.host();
+ if (host.empty()) {
+ host = std::string(request_host);
+ }
+ std::string path = push_resource.request_url.path();
+ bool found_existing_response = false;
+ {
+ QuicWriterMutexLock lock(&response_mutex_);
+ found_existing_response = QuicContainsKey(responses_, GetKey(host, path));
+ }
+ if (!found_existing_response) {
+ // Add a server push response to responses map, if it is not in the map.
+ QuicStringPiece body = push_resource.body;
+ QUIC_DVLOG(1) << "Add response for push resource: host " << host
+ << " path " << path;
+ AddResponse(host, path, push_resource.headers.Clone(), body);
+ }
+ }
+}
+
+bool QuicMemoryCacheBackend::PushResourceExistsInCache(
+ std::string original_request_url,
+ ServerPushInfo resource) {
+ QuicWriterMutexLock lock(&response_mutex_);
+ auto resource_range =
+ server_push_resources_.equal_range(original_request_url);
+ for (auto it = resource_range.first; it != resource_range.second; ++it) {
+ ServerPushInfo push_resource = it->second;
+ if (push_resource.request_url.ToString() ==
+ resource.request_url.ToString()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h b/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h
new file mode 100644
index 00000000000..ff76c9ad41b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h
@@ -0,0 +1,198 @@
+// Copyright (c) 2012 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_TOOLS_QUIC_MEMORY_CACHE_BACKEND_H_
+#define QUICHE_QUIC_TOOLS_QUIC_MEMORY_CACHE_BACKEND_H_
+
+#include <list>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mutex.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/tools/quic_backend_response.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h"
+#include "net/third_party/quiche/src/quic/tools/quic_url.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_framer.h"
+
+namespace quic {
+
+// In-memory cache for HTTP responses.
+// Reads from disk cache generated by:
+// `wget -p --save_headers <url>`
+class QuicMemoryCacheBackend : public QuicSimpleServerBackend {
+ public:
+ // Class to manage loading a resource file into memory. There are
+ // two uses: called by InitializeBackend to load resources
+ // from files, and recursively called when said resources specify
+ // server push associations.
+ class ResourceFile {
+ public:
+ explicit ResourceFile(const std::string& file_name);
+ ResourceFile(const ResourceFile&) = delete;
+ ResourceFile& operator=(const ResourceFile&) = delete;
+ virtual ~ResourceFile();
+
+ void Read();
+
+ // |base| is |file_name_| with |cache_directory| prefix stripped.
+ void SetHostPathFromBase(QuicStringPiece base);
+
+ const std::string& file_name() { return file_name_; }
+
+ QuicStringPiece host() { return host_; }
+
+ QuicStringPiece path() { return path_; }
+
+ const spdy::SpdyHeaderBlock& spdy_headers() { return spdy_headers_; }
+
+ QuicStringPiece body() { return body_; }
+
+ const std::vector<QuicStringPiece>& push_urls() { return push_urls_; }
+
+ protected:
+ void HandleXOriginalUrl();
+ QuicStringPiece RemoveScheme(QuicStringPiece url);
+
+ std::string file_name_;
+ std::string file_contents_;
+ QuicStringPiece body_;
+ spdy::SpdyHeaderBlock spdy_headers_;
+ QuicStringPiece x_original_url_;
+ std::vector<QuicStringPiece> push_urls_;
+
+ private:
+ QuicStringPiece host_;
+ QuicStringPiece path_;
+ };
+
+ QuicMemoryCacheBackend();
+ QuicMemoryCacheBackend(const QuicMemoryCacheBackend&) = delete;
+ QuicMemoryCacheBackend& operator=(const QuicMemoryCacheBackend&) = delete;
+ ~QuicMemoryCacheBackend() override;
+
+ // Retrieve a response from this cache for a given host and path..
+ // If no appropriate response exists, nullptr is returned.
+ const QuicBackendResponse* GetResponse(QuicStringPiece host,
+ QuicStringPiece path) const;
+
+ // Adds a simple response to the cache. The response headers will
+ // only contain the "content-length" header with the length of |body|.
+ void AddSimpleResponse(QuicStringPiece host,
+ QuicStringPiece path,
+ int response_code,
+ QuicStringPiece body);
+
+ // Add a simple response to the cache as AddSimpleResponse() does, and add
+ // some server push resources(resource path, corresponding response status and
+ // path) associated with it.
+ // Push resource implicitly come from the same host.
+ void AddSimpleResponseWithServerPushResources(
+ QuicStringPiece host,
+ QuicStringPiece path,
+ int response_code,
+ QuicStringPiece body,
+ std::list<QuicBackendResponse::ServerPushInfo> push_resources);
+
+ // Add a response to the cache.
+ void AddResponse(QuicStringPiece host,
+ QuicStringPiece path,
+ spdy::SpdyHeaderBlock response_headers,
+ QuicStringPiece response_body);
+
+ // Add a response, with trailers, to the cache.
+ void AddResponse(QuicStringPiece host,
+ QuicStringPiece path,
+ spdy::SpdyHeaderBlock response_headers,
+ QuicStringPiece response_body,
+ spdy::SpdyHeaderBlock response_trailers);
+
+ // Simulate a special behavior at a particular path.
+ void AddSpecialResponse(
+ QuicStringPiece host,
+ QuicStringPiece path,
+ QuicBackendResponse::SpecialResponseType response_type);
+
+ void AddSpecialResponse(
+ QuicStringPiece host,
+ QuicStringPiece path,
+ spdy::SpdyHeaderBlock response_headers,
+ QuicStringPiece response_body,
+ QuicBackendResponse::SpecialResponseType response_type);
+
+ void AddStopSendingResponse(QuicStringPiece host,
+ QuicStringPiece path,
+ spdy::SpdyHeaderBlock response_headers,
+ QuicStringPiece response_body,
+ uint16_t stop_sending_code);
+
+ // Sets a default response in case of cache misses. Takes ownership of
+ // 'response'.
+ void AddDefaultResponse(QuicBackendResponse* response);
+
+ // |cache_cirectory| can be generated using `wget -p --save-headers <url>`.
+ void InitializeFromDirectory(const std::string& cache_directory);
+
+ // Find all the server push resources associated with |request_url|.
+ std::list<QuicBackendResponse::ServerPushInfo> GetServerPushResources(
+ std::string request_url);
+
+ // Implements the functions for interface QuicSimpleServerBackend
+ // |cache_cirectory| can be generated using `wget -p --save-headers <url>`.
+ bool InitializeBackend(const std::string& cache_directory) override;
+ bool IsBackendInitialized() const override;
+ void FetchResponseFromBackend(
+ const spdy::SpdyHeaderBlock& request_headers,
+ const std::string& request_body,
+ QuicSimpleServerBackend::RequestHandler* quic_server_stream) override;
+ void CloseBackendResponseStream(
+ QuicSimpleServerBackend::RequestHandler* quic_server_stream) override;
+
+ private:
+ void AddResponseImpl(QuicStringPiece host,
+ QuicStringPiece path,
+ QuicBackendResponse::SpecialResponseType response_type,
+ spdy::SpdyHeaderBlock response_headers,
+ QuicStringPiece response_body,
+ spdy::SpdyHeaderBlock response_trailers,
+ uint16_t stop_sending_code);
+
+ std::string GetKey(QuicStringPiece host, QuicStringPiece path) const;
+
+ // Add some server push urls with given responses for specified
+ // request if these push resources are not associated with this request yet.
+ void MaybeAddServerPushResources(
+ QuicStringPiece request_host,
+ QuicStringPiece request_path,
+ std::list<QuicBackendResponse::ServerPushInfo> push_resources);
+
+ // Check if push resource(push_host/push_path) associated with given request
+ // url already exists in server push map.
+ bool PushResourceExistsInCache(std::string original_request_url,
+ QuicBackendResponse::ServerPushInfo resource);
+
+ // Cached responses.
+ QuicUnorderedMap<std::string, std::unique_ptr<QuicBackendResponse>> responses_
+ GUARDED_BY(response_mutex_);
+
+ // The default response for cache misses, if set.
+ std::unique_ptr<QuicBackendResponse> default_response_
+ GUARDED_BY(response_mutex_);
+
+ // A map from request URL to associated server push responses (if any).
+ std::multimap<std::string, QuicBackendResponse::ServerPushInfo>
+ server_push_resources_ GUARDED_BY(response_mutex_);
+
+ // Protects against concurrent access from test threads setting responses, and
+ // server threads accessing those responses.
+ mutable QuicMutex response_mutex_;
+ bool cache_initialized_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_MEMORY_CACHE_BACKEND_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend_test.cc
new file mode 100644
index 00000000000..80b1b293206
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend_test.cc
@@ -0,0 +1,235 @@
+// Copyright 2013 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/tools/quic_memory_cache_backend.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/tools/quic_backend_response.h"
+
+namespace quic {
+namespace test {
+
+namespace {
+typedef QuicBackendResponse Response;
+typedef QuicBackendResponse::ServerPushInfo ServerPushInfo;
+} // namespace
+
+class QuicMemoryCacheBackendTest : public QuicTest {
+ protected:
+ void CreateRequest(std::string host,
+ std::string path,
+ spdy::SpdyHeaderBlock* headers) {
+ (*headers)[":method"] = "GET";
+ (*headers)[":path"] = path;
+ (*headers)[":authority"] = host;
+ (*headers)[":scheme"] = "https";
+ }
+
+ std::string CacheDirectory() { return QuicGetTestMemoryCachePath(); }
+
+ QuicMemoryCacheBackend cache_;
+};
+
+TEST_F(QuicMemoryCacheBackendTest, GetResponseNoMatch) {
+ const Response* response =
+ cache_.GetResponse("mail.google.com", "/index.html");
+ ASSERT_FALSE(response);
+}
+
+TEST_F(QuicMemoryCacheBackendTest, AddSimpleResponseGetResponse) {
+ std::string response_body("hello response");
+ cache_.AddSimpleResponse("www.google.com", "/", 200, response_body);
+
+ spdy::SpdyHeaderBlock request_headers;
+ CreateRequest("www.google.com", "/", &request_headers);
+ const Response* response = cache_.GetResponse("www.google.com", "/");
+ ASSERT_TRUE(response);
+ ASSERT_TRUE(QuicContainsKey(response->headers(), ":status"));
+ EXPECT_EQ("200", response->headers().find(":status")->second);
+ EXPECT_EQ(response_body.size(), response->body().length());
+}
+
+TEST_F(QuicMemoryCacheBackendTest, AddResponse) {
+ const std::string kRequestHost = "www.foo.com";
+ const std::string kRequestPath = "/";
+ const std::string kResponseBody("hello response");
+
+ spdy::SpdyHeaderBlock response_headers;
+ response_headers[":version"] = "HTTP/1.1";
+ response_headers[":status"] = "200";
+ response_headers["content-length"] =
+ QuicTextUtils::Uint64ToString(kResponseBody.size());
+
+ spdy::SpdyHeaderBlock response_trailers;
+ response_trailers["key-1"] = "value-1";
+ response_trailers["key-2"] = "value-2";
+ response_trailers["key-3"] = "value-3";
+
+ cache_.AddResponse(kRequestHost, "/", response_headers.Clone(), kResponseBody,
+ response_trailers.Clone());
+
+ const Response* response = cache_.GetResponse(kRequestHost, kRequestPath);
+ EXPECT_EQ(response->headers(), response_headers);
+ EXPECT_EQ(response->body(), kResponseBody);
+ EXPECT_EQ(response->trailers(), response_trailers);
+}
+
+TEST_F(QuicMemoryCacheBackendTest, ReadsCacheDir) {
+ cache_.InitializeBackend(CacheDirectory());
+ const Response* response =
+ cache_.GetResponse("test.example.com", "/index.html");
+ ASSERT_TRUE(response);
+ ASSERT_TRUE(QuicContainsKey(response->headers(), ":status"));
+ EXPECT_EQ("200", response->headers().find(":status")->second);
+ // Connection headers are not valid in HTTP/2.
+ EXPECT_FALSE(QuicContainsKey(response->headers(), "connection"));
+ EXPECT_LT(0U, response->body().length());
+}
+
+TEST_F(QuicMemoryCacheBackendTest, ReadsCacheDirWithServerPushResource) {
+ cache_.InitializeBackend(CacheDirectory() + "_with_push");
+ std::list<ServerPushInfo> resources =
+ cache_.GetServerPushResources("test.example.com/");
+ ASSERT_EQ(1UL, resources.size());
+}
+
+TEST_F(QuicMemoryCacheBackendTest, ReadsCacheDirWithServerPushResources) {
+ cache_.InitializeBackend(CacheDirectory() + "_with_push");
+ std::list<ServerPushInfo> resources =
+ cache_.GetServerPushResources("test.example.com/index2.html");
+ ASSERT_EQ(2UL, resources.size());
+}
+
+TEST_F(QuicMemoryCacheBackendTest, UsesOriginalUrl) {
+ cache_.InitializeBackend(CacheDirectory());
+ const Response* response =
+ cache_.GetResponse("test.example.com", "/site_map.html");
+ ASSERT_TRUE(response);
+ ASSERT_TRUE(QuicContainsKey(response->headers(), ":status"));
+ EXPECT_EQ("200", response->headers().find(":status")->second);
+ // Connection headers are not valid in HTTP/2.
+ EXPECT_FALSE(QuicContainsKey(response->headers(), "connection"));
+ EXPECT_LT(0U, response->body().length());
+}
+
+TEST_F(QuicMemoryCacheBackendTest, DefaultResponse) {
+ // Verify GetResponse returns nullptr when no default is set.
+ const Response* response = cache_.GetResponse("www.google.com", "/");
+ ASSERT_FALSE(response);
+
+ // Add a default response.
+ spdy::SpdyHeaderBlock response_headers;
+ response_headers[":version"] = "HTTP/1.1";
+ response_headers[":status"] = "200";
+ response_headers["content-length"] = "0";
+ Response* default_response = new Response;
+ default_response->set_headers(std::move(response_headers));
+ cache_.AddDefaultResponse(default_response);
+
+ // Now we should get the default response for the original request.
+ response = cache_.GetResponse("www.google.com", "/");
+ ASSERT_TRUE(response);
+ ASSERT_TRUE(QuicContainsKey(response->headers(), ":status"));
+ EXPECT_EQ("200", response->headers().find(":status")->second);
+
+ // Now add a set response for / and make sure it is returned
+ cache_.AddSimpleResponse("www.google.com", "/", 302, "");
+ response = cache_.GetResponse("www.google.com", "/");
+ ASSERT_TRUE(response);
+ ASSERT_TRUE(QuicContainsKey(response->headers(), ":status"));
+ EXPECT_EQ("302", response->headers().find(":status")->second);
+
+ // We should get the default response for other requests.
+ response = cache_.GetResponse("www.google.com", "/asd");
+ ASSERT_TRUE(response);
+ ASSERT_TRUE(QuicContainsKey(response->headers(), ":status"));
+ EXPECT_EQ("200", response->headers().find(":status")->second);
+}
+
+TEST_F(QuicMemoryCacheBackendTest, AddSimpleResponseWithServerPushResources) {
+ std::string request_host = "www.foo.com";
+ std::string response_body("hello response");
+ const size_t kNumResources = 5;
+ int NumResources = 5;
+ std::list<ServerPushInfo> push_resources;
+ std::string scheme = "http";
+ for (int i = 0; i < NumResources; ++i) {
+ std::string path = "/server_push_src" + QuicTextUtils::Uint64ToString(i);
+ std::string url = scheme + "://" + request_host + path;
+ QuicUrl resource_url(url);
+ std::string body =
+ QuicStrCat("This is server push response body for ", path);
+ spdy::SpdyHeaderBlock response_headers;
+ response_headers[":version"] = "HTTP/1.1";
+ response_headers[":status"] = "200";
+ response_headers["content-length"] =
+ QuicTextUtils::Uint64ToString(body.size());
+ push_resources.push_back(
+ ServerPushInfo(resource_url, response_headers.Clone(), i, body));
+ }
+
+ cache_.AddSimpleResponseWithServerPushResources(
+ request_host, "/", 200, response_body, push_resources);
+
+ std::string request_url = request_host + "/";
+ std::list<ServerPushInfo> resources =
+ cache_.GetServerPushResources(request_url);
+ ASSERT_EQ(kNumResources, resources.size());
+ for (const auto& push_resource : push_resources) {
+ ServerPushInfo resource = resources.front();
+ EXPECT_EQ(resource.request_url.ToString(),
+ push_resource.request_url.ToString());
+ EXPECT_EQ(resource.priority, push_resource.priority);
+ resources.pop_front();
+ }
+}
+
+TEST_F(QuicMemoryCacheBackendTest, GetServerPushResourcesAndPushResponses) {
+ std::string request_host = "www.foo.com";
+ std::string response_body("hello response");
+ const size_t kNumResources = 4;
+ int NumResources = 4;
+ std::string scheme = "http";
+ std::string push_response_status[kNumResources] = {"200", "200", "301",
+ "404"};
+ std::list<ServerPushInfo> push_resources;
+ for (int i = 0; i < NumResources; ++i) {
+ std::string path = "/server_push_src" + QuicTextUtils::Uint64ToString(i);
+ std::string url = scheme + "://" + request_host + path;
+ QuicUrl resource_url(url);
+ std::string body = "This is server push response body for " + path;
+ spdy::SpdyHeaderBlock response_headers;
+ response_headers[":version"] = "HTTP/1.1";
+ response_headers[":status"] = push_response_status[i];
+ response_headers["content-length"] =
+ QuicTextUtils::Uint64ToString(body.size());
+ push_resources.push_back(
+ ServerPushInfo(resource_url, response_headers.Clone(), i, body));
+ }
+ cache_.AddSimpleResponseWithServerPushResources(
+ request_host, "/", 200, response_body, push_resources);
+ std::string request_url = request_host + "/";
+ std::list<ServerPushInfo> resources =
+ cache_.GetServerPushResources(request_url);
+ ASSERT_EQ(kNumResources, resources.size());
+ int i = 0;
+ for (const auto& push_resource : push_resources) {
+ QuicUrl url = resources.front().request_url;
+ std::string host = url.host();
+ std::string path = url.path();
+ const Response* response = cache_.GetResponse(host, path);
+ ASSERT_TRUE(response);
+ ASSERT_TRUE(QuicContainsKey(response->headers(), ":status"));
+ EXPECT_EQ(push_response_status[i++],
+ response->headers().find(":status")->second);
+ EXPECT_EQ(push_resource.body, response->body());
+ resources.pop_front();
+ }
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_packet_printer_bin.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_packet_printer_bin.cc
new file mode 100644
index 00000000000..c8cfc11ec8f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_packet_printer_bin.cc
@@ -0,0 +1,246 @@
+// Copyright (c) 2012 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.
+
+// clang-format off
+
+// Dumps out the decryptable contents of a QUIC packet in a human-readable way.
+// If the packet is null encrypted, this will dump full packet contents.
+// Otherwise it will dump the header, and fail with an error that the
+// packet is undecryptable.
+//
+// Usage: quic_packet_printer server|client <hex dump of packet>
+//
+// Example input:
+// quic_packet_printer server 0c6b810308320f24c004a939a38a2e3fd6ca589917f200400201b80b0100501c0700060003023d0000001c00556e656e637279707465642073747265616d2064617461207365656e
+//
+// Example output:
+// OnPacket
+// OnUnauthenticatedPublicHeader
+// OnUnauthenticatedHeader: { connection_id: 13845207862000976235, connection_id_length:8, packet_number_length:1, multipath_flag: 0, reset_flag: 0, version_flag: 0, path_id: , packet_number: 4 }
+// OnDecryptedPacket
+// OnPacketHeader
+// OnAckFrame: largest_observed: 1 ack_delay_time: 3000 missing_packets: [ ] is_truncated: 0 received_packets: [ 1 at 466016 ]
+// OnStopWaitingFrame
+// OnConnectionCloseFrame: error_code { 61 } error_details { Unencrypted stream data seen }
+
+// clang-format on
+
+#include <iostream>
+
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(std::string,
+ quic_version,
+ "",
+ "If set, specify the QUIC version to use.");
+
+namespace quic {
+
+class QuicPacketPrinter : public QuicFramerVisitorInterface {
+ public:
+ explicit QuicPacketPrinter(QuicFramer* framer) : framer_(framer) {}
+
+ void OnError(QuicFramer* framer) override {
+ std::cerr << "OnError: " << QuicErrorCodeToString(framer->error())
+ << " detail: " << framer->detailed_error() << "\n";
+ }
+ bool OnProtocolVersionMismatch(ParsedQuicVersion received_version,
+ PacketHeaderFormat form) override {
+ framer_->set_version(received_version);
+ std::cerr << "OnProtocolVersionMismatch: "
+ << ParsedQuicVersionToString(received_version) << "\n";
+ return true;
+ }
+ void OnPacket() override { std::cerr << "OnPacket\n"; }
+ void OnPublicResetPacket(const QuicPublicResetPacket& packet) override {
+ std::cerr << "OnPublicResetPacket\n";
+ }
+ void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& packet) override {
+ std::cerr << "OnVersionNegotiationPacket\n";
+ }
+ bool OnUnauthenticatedPublicHeader(const QuicPacketHeader& header) override {
+ std::cerr << "OnUnauthenticatedPublicHeader\n";
+ return true;
+ }
+ bool OnUnauthenticatedHeader(const QuicPacketHeader& header) override {
+ std::cerr << "OnUnauthenticatedHeader: " << header;
+ return true;
+ }
+ void OnDecryptedPacket(EncryptionLevel level) override {
+ // This only currently supports "decrypting" null encrypted packets.
+ DCHECK_EQ(ENCRYPTION_INITIAL, level);
+ std::cerr << "OnDecryptedPacket\n";
+ }
+ bool OnPacketHeader(const QuicPacketHeader& header) override {
+ std::cerr << "OnPacketHeader\n";
+ return true;
+ }
+ void OnCoalescedPacket(const QuicEncryptedPacket& packet) override {
+ std::cerr << "OnCoalescedPacket\n";
+ }
+ bool OnStreamFrame(const QuicStreamFrame& frame) override {
+ std::cerr << "OnStreamFrame: " << frame;
+ std::cerr << " data: { "
+ << QuicTextUtils::HexEncode(frame.data_buffer, frame.data_length)
+ << " }\n";
+ return true;
+ }
+ bool OnCryptoFrame(const QuicCryptoFrame& frame) override {
+ std::cerr << "OnCryptoFrame: " << frame;
+ std::cerr << " data: { "
+ << QuicTextUtils::HexEncode(frame.data_buffer, frame.data_length)
+ << " }\n";
+ return true;
+ }
+ bool OnAckFrameStart(QuicPacketNumber largest_acked,
+ QuicTime::Delta /*ack_delay_time*/) override {
+ std::cerr << "OnAckFrameStart, largest_acked: " << largest_acked;
+ return true;
+ }
+ bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override {
+ std::cerr << "OnAckRange: [" << start << ", " << end << ")";
+ return true;
+ }
+ bool OnAckTimestamp(QuicPacketNumber packet_number,
+ QuicTime timestamp) override {
+ std::cerr << "OnAckTimestamp: [" << packet_number << ", "
+ << timestamp.ToDebuggingValue() << ")";
+ return true;
+ }
+ bool OnAckFrameEnd(QuicPacketNumber start) override {
+ std::cerr << "OnAckFrameEnd, start: " << start;
+ return true;
+ }
+ bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override {
+ std::cerr << "OnStopWaitingFrame: " << frame;
+ return true;
+ }
+ bool OnPaddingFrame(const QuicPaddingFrame& frame) override {
+ std::cerr << "OnPaddingFrame: " << frame;
+ return true;
+ }
+ bool OnPingFrame(const QuicPingFrame& frame) override {
+ std::cerr << "OnPingFrame\n";
+ return true;
+ }
+ bool OnRstStreamFrame(const QuicRstStreamFrame& frame) override {
+ std::cerr << "OnRstStreamFrame: " << frame;
+ return true;
+ }
+ bool OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) override {
+ // The frame printout will indicate whether it's a Google QUIC
+ // CONNECTION_CLOSE, IETF QUIC CONNECTION_CLOSE/Transport, or IETF QUIC
+ // CONNECTION_CLOSE/Application frame.
+ std::cerr << "OnConnectionCloseFrame: " << frame;
+ return true;
+ }
+ bool OnNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame) override {
+ std::cerr << "OnNewConnectionIdFrame: " << frame;
+ return true;
+ }
+ bool OnRetireConnectionIdFrame(
+ const QuicRetireConnectionIdFrame& frame) override {
+ std::cerr << "OnRetireConnectionIdFrame: " << frame;
+ return true;
+ }
+ bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override {
+ std::cerr << "OnNewTokenFrame: " << frame;
+ return true;
+ }
+ bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override {
+ std::cerr << "OnStopSendingFrame: " << frame;
+ return true;
+ }
+ bool OnPathChallengeFrame(const QuicPathChallengeFrame& frame) override {
+ std::cerr << "OnPathChallengeFrame: " << frame;
+ return true;
+ }
+ bool OnPathResponseFrame(const QuicPathResponseFrame& frame) override {
+ std::cerr << "OnPathResponseFrame: " << frame;
+ return true;
+ }
+ bool OnGoAwayFrame(const QuicGoAwayFrame& frame) override {
+ std::cerr << "OnGoAwayFrame: " << frame;
+ return true;
+ }
+ bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) override {
+ std::cerr << "OnMaxStreamIdFrame: " << frame;
+ return true;
+ }
+ bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame) override {
+ std::cerr << "OnStreamIdBlockedFrame: " << frame;
+ return true;
+ }
+ bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override {
+ std::cerr << "OnWindowUpdateFrame: " << frame;
+ return true;
+ }
+ bool OnBlockedFrame(const QuicBlockedFrame& frame) override {
+ std::cerr << "OnBlockedFrame: " << frame;
+ return true;
+ }
+ bool OnMessageFrame(const QuicMessageFrame& frame) override {
+ std::cerr << "OnMessageFrame: " << frame;
+ return true;
+ }
+ void OnPacketComplete() override { std::cerr << "OnPacketComplete\n"; }
+ bool IsValidStatelessResetToken(QuicUint128 token) const override {
+ std::cerr << "IsValidStatelessResetToken\n";
+ return false;
+ }
+ void OnAuthenticatedIetfStatelessResetPacket(
+ const QuicIetfStatelessResetPacket& packet) override {
+ std::cerr << "OnAuthenticatedIetfStatelessResetPacket\n";
+ }
+
+ private:
+ QuicFramer* framer_; // Unowned.
+};
+
+} // namespace quic
+
+int main(int argc, char* argv[]) {
+ const char* usage = "Usage: quic_packet_printer client|server <hex>";
+ std::vector<std::string> args =
+ quic::QuicParseCommandLineFlags(usage, argc, argv);
+
+ if (args.size() < 2) {
+ quic::QuicPrintCommandLineFlagHelp(usage);
+ return 1;
+ }
+
+ std::string perspective_string = args[0];
+ quic::Perspective perspective;
+ if (perspective_string == "client") {
+ perspective = quic::Perspective::IS_CLIENT;
+ } else if (perspective_string == "server") {
+ perspective = quic::Perspective::IS_SERVER;
+ } else {
+ std::cerr << "Invalid perspective" << std::endl;
+ quic::QuicPrintCommandLineFlagHelp(usage);
+ return 1;
+ }
+ std::string hex = quic::QuicTextUtils::HexDecode(args[1]);
+ quic::ParsedQuicVersionVector versions = quic::AllSupportedVersions();
+ // Fake a time since we're not actually generating acks.
+ quic::QuicTime start(quic::QuicTime::Zero());
+ quic::QuicFramer framer(versions, start, perspective,
+ quic::kQuicDefaultConnectionIdLength);
+ if (!GetQuicFlag(FLAGS_quic_version).empty()) {
+ for (quic::ParsedQuicVersion version : versions) {
+ if (quic::QuicVersionToString(version.transport_version) ==
+ GetQuicFlag(FLAGS_quic_version)) {
+ framer.set_version(version);
+ }
+ }
+ }
+ quic::QuicPacketPrinter visitor(&framer);
+ framer.set_visitor(&visitor);
+ quic::QuicEncryptedPacket encrypted(hex.c_str(), hex.length());
+ return framer.ProcessPacket(encrypted);
+}
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_reject_reason_decoder_bin.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_reject_reason_decoder_bin.cc
new file mode 100644
index 00000000000..de66774bab3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_reject_reason_decoder_bin.cc
@@ -0,0 +1,43 @@
+// Copyright (c) 2016 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.
+
+// Decodes the packet HandshakeFailureReason from the chromium histogram
+// Net.QuicClientHelloRejectReasons
+
+#include <iostream>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using quic::CryptoUtils;
+using quic::HandshakeFailureReason;
+using quic::MAX_FAILURE_REASON;
+
+int main(int argc, char* argv[]) {
+ const char* usage = "Usage: quic_reject_reason_decoder <packed_reason>";
+ std::vector<std::string> args =
+ quic::QuicParseCommandLineFlags(usage, argc, argv);
+
+ if (args.size() != 1) {
+ std::cerr << usage << std::endl;
+ return 1;
+ }
+
+ uint32_t packed_error = 0;
+ if (!quic::QuicTextUtils::StringToUint32(args[0], &packed_error)) {
+ std::cerr << "Unable to parse: " << args[0] << "\n";
+ return 2;
+ }
+
+ for (int i = 1; i < MAX_FAILURE_REASON; ++i) {
+ if ((packed_error & (1 << (i - 1))) == 0) {
+ continue;
+ }
+ HandshakeFailureReason reason = static_cast<HandshakeFailureReason>(i);
+ std::cout << CryptoUtils::HandshakeFailureReasonToString(reason) << "\n";
+ }
+ return 0;
+}
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_server.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_server.cc
new file mode 100644
index 00000000000..d67bc3078d0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_server.cc
@@ -0,0 +1,212 @@
+// Copyright (c) 2012 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/tools/quic_server.h"
+
+#include <errno.h>
+#include <features.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+
+#include <cstdint>
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
+#include "net/third_party/quiche/src/quic/core/quic_default_packet_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_dispatcher.h"
+#include "net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_reader.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.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/quic/platform/impl/quic_epoll_clock.h"
+#include "net/quic/platform/impl/quic_socket_utils.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h"
+
+#ifndef SO_RXQ_OVFL
+#define SO_RXQ_OVFL 40
+#endif
+
+namespace quic {
+
+namespace {
+
+const int kEpollFlags = EPOLLIN | EPOLLOUT | EPOLLET;
+const char kSourceAddressTokenSecret[] = "secret";
+
+} // namespace
+
+const size_t kNumSessionsToCreatePerSocketEvent = 16;
+
+QuicServer::QuicServer(std::unique_ptr<ProofSource> proof_source,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicServer(std::move(proof_source),
+ QuicConfig(),
+ QuicCryptoServerConfig::ConfigOptions(),
+ AllSupportedVersions(),
+ quic_simple_server_backend,
+ kQuicDefaultConnectionIdLength) {}
+
+QuicServer::QuicServer(
+ std::unique_ptr<ProofSource> proof_source,
+ const QuicConfig& config,
+ const QuicCryptoServerConfig::ConfigOptions& crypto_config_options,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicSimpleServerBackend* quic_simple_server_backend,
+ uint8_t expected_connection_id_length)
+ : port_(0),
+ fd_(-1),
+ packets_dropped_(0),
+ overflow_supported_(false),
+ silent_close_(false),
+ config_(config),
+ crypto_config_(kSourceAddressTokenSecret,
+ QuicRandom::GetInstance(),
+ std::move(proof_source),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx()),
+ crypto_config_options_(crypto_config_options),
+ version_manager_(supported_versions),
+ packet_reader_(new QuicPacketReader()),
+ quic_simple_server_backend_(quic_simple_server_backend),
+ expected_connection_id_length_(expected_connection_id_length) {
+ Initialize();
+}
+
+void QuicServer::Initialize() {
+ // If an initial flow control window has not explicitly been set, then use a
+ // sensible value for a server: 1 MB for session, 64 KB for each stream.
+ const uint32_t kInitialSessionFlowControlWindow = 1 * 1024 * 1024; // 1 MB
+ const uint32_t kInitialStreamFlowControlWindow = 64 * 1024; // 64 KB
+ if (config_.GetInitialStreamFlowControlWindowToSend() ==
+ kMinimumFlowControlSendWindow) {
+ config_.SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindow);
+ }
+ if (config_.GetInitialSessionFlowControlWindowToSend() ==
+ kMinimumFlowControlSendWindow) {
+ config_.SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindow);
+ }
+
+ epoll_server_.set_timeout_in_us(50 * 1000);
+
+ QuicEpollClock clock(&epoll_server_);
+
+ std::unique_ptr<CryptoHandshakeMessage> scfg(crypto_config_.AddDefaultConfig(
+ QuicRandom::GetInstance(), &clock, crypto_config_options_));
+}
+
+QuicServer::~QuicServer() = default;
+
+bool QuicServer::CreateUDPSocketAndListen(const QuicSocketAddress& address) {
+ fd_ = QuicSocketUtils::CreateUDPSocket(
+ address,
+ /*receive_buffer_size =*/kDefaultSocketReceiveBuffer,
+ /*send_buffer_size =*/kDefaultSocketReceiveBuffer, &overflow_supported_);
+ if (fd_ < 0) {
+ QUIC_LOG(ERROR) << "CreateSocket() failed: " << strerror(errno);
+ return false;
+ }
+
+ sockaddr_storage addr = address.generic_address();
+ int rc = bind(fd_, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
+ if (rc < 0) {
+ QUIC_LOG(ERROR) << "Bind failed: " << strerror(errno);
+ return false;
+ }
+ QUIC_LOG(INFO) << "Listening on " << address.ToString();
+ port_ = address.port();
+ if (port_ == 0) {
+ QuicSocketAddress address;
+ if (address.FromSocket(fd_) != 0) {
+ QUIC_LOG(ERROR) << "Unable to get self address. Error: "
+ << strerror(errno);
+ }
+ port_ = address.port();
+ }
+
+ epoll_server_.RegisterFD(fd_, this, kEpollFlags);
+ dispatcher_.reset(CreateQuicDispatcher());
+ dispatcher_->InitializeWithWriter(CreateWriter(fd_));
+
+ return true;
+}
+
+QuicPacketWriter* QuicServer::CreateWriter(int fd) {
+ return new QuicDefaultPacketWriter(fd);
+}
+
+QuicDispatcher* QuicServer::CreateQuicDispatcher() {
+ QuicEpollAlarmFactory alarm_factory(&epoll_server_);
+ return new QuicSimpleDispatcher(
+ &config_, &crypto_config_, &version_manager_,
+ std::unique_ptr<QuicEpollConnectionHelper>(new QuicEpollConnectionHelper(
+ &epoll_server_, QuicAllocator::BUFFER_POOL)),
+ std::unique_ptr<QuicCryptoServerStream::Helper>(
+ new QuicSimpleCryptoServerStreamHelper(QuicRandom::GetInstance())),
+ std::unique_ptr<QuicEpollAlarmFactory>(
+ new QuicEpollAlarmFactory(&epoll_server_)),
+ quic_simple_server_backend_, expected_connection_id_length_);
+}
+
+void QuicServer::WaitForEvents() {
+ epoll_server_.WaitForEventsAndExecuteCallbacks();
+}
+
+void QuicServer::Shutdown() {
+ if (!silent_close_) {
+ // Before we shut down the epoll server, give all active sessions a chance
+ // to notify clients that they're closing.
+ dispatcher_->Shutdown();
+ }
+
+ epoll_server_.Shutdown();
+
+ close(fd_);
+ fd_ = -1;
+}
+
+void QuicServer::OnEvent(int fd, QuicEpollEvent* event) {
+ DCHECK_EQ(fd, fd_);
+ event->out_ready_mask = 0;
+
+ if (event->in_events & EPOLLIN) {
+ QUIC_DVLOG(1) << "EPOLLIN";
+
+ dispatcher_->ProcessBufferedChlos(kNumSessionsToCreatePerSocketEvent);
+
+ bool more_to_read = true;
+ while (more_to_read) {
+ more_to_read = packet_reader_->ReadAndDispatchPackets(
+ fd_, port_, QuicEpollClock(&epoll_server_), dispatcher_.get(),
+ overflow_supported_ ? &packets_dropped_ : nullptr);
+ }
+
+ if (dispatcher_->HasChlosBuffered()) {
+ // Register EPOLLIN event to consume buffered CHLO(s).
+ event->out_ready_mask |= EPOLLIN;
+ }
+ }
+ if (event->in_events & EPOLLOUT) {
+ dispatcher_->OnCanWrite();
+ if (dispatcher_->HasPendingWrites()) {
+ event->out_ready_mask |= EPOLLOUT;
+ }
+ }
+ if (event->in_events & EPOLLERR) {
+ }
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_server.h b/chromium/net/third_party/quiche/src/quic/tools/quic_server.h
new file mode 100644
index 00000000000..4d7e08199a9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_server.h
@@ -0,0 +1,159 @@
+// Copyright (c) 2012 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.
+
+// A toy server, which listens on a specified address for QUIC traffic and
+// handles incoming responses.
+//
+// Note that this server is intended to verify correctness of the client and is
+// in no way expected to be performant.
+
+#ifndef QUICHE_QUIC_TOOLS_QUIC_SERVER_H_
+#define QUICHE_QUIC_TOOLS_QUIC_SERVER_H_
+
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_version_manager.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h"
+
+namespace quic {
+
+namespace test {
+class QuicServerPeer;
+} // namespace test
+
+class QuicDispatcher;
+class QuicPacketReader;
+
+class QuicServer : public QuicEpollCallbackInterface {
+ public:
+ QuicServer(std::unique_ptr<ProofSource> proof_source,
+ QuicSimpleServerBackend* quic_simple_server_backend);
+ QuicServer(std::unique_ptr<ProofSource> proof_source,
+ const QuicConfig& config,
+ const QuicCryptoServerConfig::ConfigOptions& server_config_options,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicSimpleServerBackend* quic_simple_server_backend,
+ uint8_t expected_connection_id_length);
+ QuicServer(const QuicServer&) = delete;
+ QuicServer& operator=(const QuicServer&) = delete;
+
+ ~QuicServer() override;
+
+ std::string Name() const override { return "QuicServer"; }
+
+ // Start listening on the specified address.
+ bool CreateUDPSocketAndListen(const QuicSocketAddress& address);
+
+ // Wait up to 50ms, and handle any events which occur.
+ void WaitForEvents();
+
+ // Server deletion is imminent. Start cleaning up the epoll server.
+ virtual void Shutdown();
+
+ // From EpollCallbackInterface
+ void OnRegistration(QuicEpollServer* eps, int fd, int event_mask) override {}
+ void OnModification(int fd, int event_mask) override {}
+ void OnEvent(int fd, QuicEpollEvent* event) override;
+ void OnUnregistration(int fd, bool replaced) override {}
+
+ void OnShutdown(QuicEpollServer* eps, int fd) override {}
+
+ void SetChloMultiplier(size_t multiplier) {
+ crypto_config_.set_chlo_multiplier(multiplier);
+ }
+
+ void SetPreSharedKey(QuicStringPiece key) {
+ crypto_config_.set_pre_shared_key(key);
+ }
+
+ bool overflow_supported() { return overflow_supported_; }
+
+ QuicPacketCount packets_dropped() { return packets_dropped_; }
+
+ int port() { return port_; }
+
+ protected:
+ virtual QuicPacketWriter* CreateWriter(int fd);
+
+ virtual QuicDispatcher* CreateQuicDispatcher();
+
+ const QuicConfig& config() const { return config_; }
+ const QuicCryptoServerConfig& crypto_config() const { return crypto_config_; }
+ QuicEpollServer* epoll_server() { return &epoll_server_; }
+
+ QuicDispatcher* dispatcher() { return dispatcher_.get(); }
+
+ QuicVersionManager* version_manager() { return &version_manager_; }
+
+ QuicSimpleServerBackend* server_backend() {
+ return quic_simple_server_backend_;
+ }
+
+ void set_silent_close(bool value) { silent_close_ = value; }
+
+ uint8_t expected_connection_id_length() {
+ return expected_connection_id_length_;
+ }
+
+ private:
+ friend class quic::test::QuicServerPeer;
+
+ // Initialize the internal state of the server.
+ void Initialize();
+
+ // Accepts data from the framer and demuxes clients to sessions.
+ std::unique_ptr<QuicDispatcher> dispatcher_;
+ // Frames incoming packets and hands them to the dispatcher.
+ QuicEpollServer epoll_server_;
+
+ // The port the server is listening on.
+ int port_;
+
+ // Listening connection. Also used for outbound client communication.
+ int fd_;
+
+ // If overflow_supported_ is true this will be the number of packets dropped
+ // during the lifetime of the server. This may overflow if enough packets
+ // are dropped.
+ QuicPacketCount packets_dropped_;
+
+ // True if the kernel supports SO_RXQ_OVFL, the number of packets dropped
+ // because the socket would otherwise overflow.
+ bool overflow_supported_;
+
+ // If true, do not call Shutdown on the dispatcher. Connections will close
+ // without sending a final connection close.
+ bool silent_close_;
+
+ // config_ contains non-crypto parameters that are negotiated in the crypto
+ // handshake.
+ QuicConfig config_;
+ // crypto_config_ contains crypto parameters for the handshake.
+ QuicCryptoServerConfig crypto_config_;
+ // crypto_config_options_ contains crypto parameters for the handshake.
+ QuicCryptoServerConfig::ConfigOptions crypto_config_options_;
+
+ // Used to generate current supported versions.
+ QuicVersionManager version_manager_;
+
+ // Point to a QuicPacketReader object on the heap. The reader allocates more
+ // space than allowed on the stack.
+ std::unique_ptr<QuicPacketReader> packet_reader_;
+
+ QuicSimpleServerBackend* quic_simple_server_backend_; // unowned.
+
+ // Connection ID length expected to be read on incoming IETF short headers.
+ uint8_t expected_connection_id_length_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_SERVER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_server_bin.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_server_bin.cc
new file mode 100644
index 00000000000..128c873dfda
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_server_bin.cc
@@ -0,0 +1,70 @@
+// Copyright 2014 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.
+
+// A binary wrapper for QuicServer. It listens forever on --port
+// (default 6121) until it's killed or ctrl-cd to death.
+
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/quic_versions.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/tools/quic_memory_cache_backend.h"
+#include "net/third_party/quiche/src/quic/tools/quic_server.h"
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t,
+ port,
+ 6121,
+ "The port the quic server will listen on.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+ std::string,
+ quic_response_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(
+ int32_t,
+ quic_ietf_draft,
+ 0,
+ "QUIC IETF draft number to use over the wire, e.g. 18. "
+ "This also enables required internal QUIC flags.");
+
+int main(int argc, char* argv[]) {
+ const char* usage = "Usage: quic_server [options]";
+ std::vector<std::string> non_option_args =
+ quic::QuicParseCommandLineFlags(usage, argc, argv);
+ if (!non_option_args.empty()) {
+ quic::QuicPrintCommandLineFlagHelp(usage);
+ exit(0);
+ }
+
+ const int32_t quic_ietf_draft = GetQuicFlag(FLAGS_quic_ietf_draft);
+ if (quic_ietf_draft > 0) {
+ quic::QuicVersionInitializeSupportForIetfDraft(quic_ietf_draft);
+ quic::QuicEnableVersion(
+ quic::ParsedQuicVersion(quic::PROTOCOL_TLS1_3, quic::QUIC_VERSION_99));
+ }
+
+ quic::QuicMemoryCacheBackend memory_cache_backend;
+ if (!GetQuicFlag(FLAGS_quic_response_cache_dir).empty()) {
+ memory_cache_backend.InitializeBackend(
+ GetQuicFlag(FLAGS_quic_response_cache_dir));
+ }
+
+ quic::QuicServer server(quic::CreateDefaultProofSource(),
+ &memory_cache_backend);
+
+ if (!server.CreateUDPSocketAndListen(quic::QuicSocketAddress(
+ quic::QuicIpAddress::Any6(), GetQuicFlag(FLAGS_port)))) {
+ return 1;
+ }
+
+ while (true) {
+ server.WaitForEvents();
+ }
+}
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_server_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_server_test.cc
new file mode 100644
index 00000000000..bb007cbb49a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_server_test.cc
@@ -0,0 +1,215 @@
+// Copyright 2013 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/tools/quic_server.h"
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.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_port_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test_loopback.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_quic_dispatcher.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_server_peer.h"
+#include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h"
+
+namespace quic {
+namespace test {
+
+using ::testing::_;
+
+namespace {
+
+class MockQuicSimpleDispatcher : public QuicSimpleDispatcher {
+ public:
+ MockQuicSimpleDispatcher(
+ 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,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicSimpleDispatcher(config,
+ crypto_config,
+ version_manager,
+ std::move(helper),
+ std::move(session_helper),
+ std::move(alarm_factory),
+ quic_simple_server_backend,
+ kQuicDefaultConnectionIdLength) {}
+ ~MockQuicSimpleDispatcher() override = default;
+
+ MOCK_METHOD0(OnCanWrite, void());
+ MOCK_CONST_METHOD0(HasPendingWrites, bool());
+ MOCK_CONST_METHOD0(HasChlosBuffered, bool());
+ MOCK_METHOD1(ProcessBufferedChlos, void(size_t));
+};
+
+class TestQuicServer : public QuicServer {
+ public:
+ TestQuicServer()
+ : QuicServer(crypto_test_utils::ProofSourceForTesting(),
+ &quic_simple_server_backend_) {}
+
+ ~TestQuicServer() override = default;
+
+ MockQuicSimpleDispatcher* mock_dispatcher() { return mock_dispatcher_; }
+
+ protected:
+ QuicDispatcher* CreateQuicDispatcher() override {
+ mock_dispatcher_ = new MockQuicSimpleDispatcher(
+ &config(), &crypto_config(), version_manager(),
+ std::unique_ptr<QuicEpollConnectionHelper>(
+ new QuicEpollConnectionHelper(epoll_server(),
+ QuicAllocator::BUFFER_POOL)),
+ std::unique_ptr<QuicCryptoServerStream::Helper>(
+ new QuicSimpleCryptoServerStreamHelper(QuicRandom::GetInstance())),
+ std::unique_ptr<QuicEpollAlarmFactory>(
+ new QuicEpollAlarmFactory(epoll_server())),
+ &quic_simple_server_backend_);
+ return mock_dispatcher_;
+ }
+
+ MockQuicSimpleDispatcher* mock_dispatcher_ = nullptr;
+ QuicMemoryCacheBackend quic_simple_server_backend_;
+};
+
+class QuicServerEpollInTest : public QuicTest {
+ public:
+ QuicServerEpollInTest()
+ : port_(QuicPickUnusedPortOrDie()),
+ server_address_(TestLoopback(), port_) {}
+
+ void StartListening() {
+ server_.CreateUDPSocketAndListen(server_address_);
+ ASSERT_TRUE(QuicServerPeer::SetSmallSocket(&server_));
+
+ if (!server_.overflow_supported()) {
+ QUIC_LOG(WARNING) << "Overflow not supported. Not testing.";
+ return;
+ }
+ }
+
+ protected:
+ int port_;
+ QuicSocketAddress server_address_;
+ TestQuicServer server_;
+};
+
+// Tests that if dispatcher has CHLOs waiting for connection creation, EPOLLIN
+// event should try to create connections for them. And set epoll mask with
+// EPOLLIN if there are still CHLOs remaining at the end of epoll event.
+TEST_F(QuicServerEpollInTest, ProcessBufferedCHLOsOnEpollin) {
+ // Given an EPOLLIN event, try to create session for buffered CHLOs. In first
+ // event, dispatcher can't create session for all of CHLOs. So listener should
+ // register another EPOLLIN event by itself. Even without new packet arrival,
+ // the rest CHLOs should be process in next epoll event.
+ StartListening();
+ bool more_chlos = true;
+ MockQuicSimpleDispatcher* dispatcher_ = server_.mock_dispatcher();
+ DCHECK(dispatcher_ != nullptr);
+ EXPECT_CALL(*dispatcher_, OnCanWrite()).Times(testing::AnyNumber());
+ EXPECT_CALL(*dispatcher_, ProcessBufferedChlos(_)).Times(2);
+ EXPECT_CALL(*dispatcher_, HasPendingWrites()).Times(testing::AnyNumber());
+ // Expect there are still CHLOs buffered after 1st event. But not any more
+ // after 2nd event.
+ EXPECT_CALL(*dispatcher_, HasChlosBuffered())
+ .WillOnce(testing::Return(true))
+ .WillOnce(
+ DoAll(testing::Assign(&more_chlos, false), testing::Return(false)));
+
+ // Send a packet to trigger epoll event.
+ int fd = socket(
+ AddressFamilyUnderTest() == IpAddressFamily::IP_V4 ? AF_INET : AF_INET6,
+ SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
+ ASSERT_LT(0, fd);
+
+ char buf[1024];
+ memset(buf, 0, QUIC_ARRAYSIZE(buf));
+ sockaddr_storage storage = server_address_.generic_address();
+ int rc = sendto(fd, buf, QUIC_ARRAYSIZE(buf), 0,
+ reinterpret_cast<sockaddr*>(&storage), sizeof(storage));
+ if (rc < 0) {
+ QUIC_DLOG(INFO) << errno << " " << strerror(errno);
+ }
+
+ while (more_chlos) {
+ server_.WaitForEvents();
+ }
+}
+
+class QuicServerDispatchPacketTest : public QuicTest {
+ public:
+ QuicServerDispatchPacketTest()
+ : crypto_config_("blah",
+ QuicRandom::GetInstance(),
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx()),
+ version_manager_(AllSupportedVersions()),
+ dispatcher_(
+ &config_,
+ &crypto_config_,
+ &version_manager_,
+ std::unique_ptr<QuicEpollConnectionHelper>(
+ new QuicEpollConnectionHelper(&eps_,
+ QuicAllocator::BUFFER_POOL)),
+ std::unique_ptr<QuicCryptoServerStream::Helper>(
+ new QuicSimpleCryptoServerStreamHelper(
+ QuicRandom::GetInstance())),
+ std::unique_ptr<QuicEpollAlarmFactory>(
+ new QuicEpollAlarmFactory(&eps_)),
+ &quic_simple_server_backend_) {
+ dispatcher_.InitializeWithWriter(new QuicDefaultPacketWriter(1234));
+ }
+
+ void DispatchPacket(const QuicReceivedPacket& packet) {
+ QuicSocketAddress client_addr, server_addr;
+ dispatcher_.ProcessPacket(server_addr, client_addr, packet);
+ }
+
+ protected:
+ QuicConfig config_;
+ QuicCryptoServerConfig crypto_config_;
+ QuicVersionManager version_manager_;
+ QuicEpollServer eps_;
+ QuicMemoryCacheBackend quic_simple_server_backend_;
+ MockQuicDispatcher dispatcher_;
+};
+
+TEST_F(QuicServerDispatchPacketTest, DispatchPacket) {
+ // clang-format off
+ unsigned char valid_packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00
+ };
+ // clang-format on
+ QuicReceivedPacket encrypted_valid_packet(
+ reinterpret_cast<char*>(valid_packet), QUIC_ARRAYSIZE(valid_packet),
+ QuicTime::Zero(), false);
+
+ EXPECT_CALL(dispatcher_, ProcessPacket(_, _, _)).Times(1);
+ DispatchPacket(encrypted_valid_packet);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.cc
new file mode 100644
index 00000000000..7cc875b5a63
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.cc
@@ -0,0 +1,18 @@
+// Copyright (c) 2018 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/tools/quic_simple_client_session.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+
+namespace quic {
+
+std::unique_ptr<QuicSpdyClientStream>
+QuicSimpleClientSession::CreateClientStream() {
+ return QuicMakeUnique<QuicSimpleClientStream>(
+ GetNextOutgoingBidirectionalStreamId(), this, BIDIRECTIONAL,
+ drop_response_body_);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.h b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.h
new file mode 100644
index 00000000000..1a17f3d74f3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2018 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_TOOLS_QUIC_SIMPLE_CLIENT_SESSION_H_
+#define QUICHE_QUIC_TOOLS_QUIC_SIMPLE_CLIENT_SESSION_H_
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_client_stream.h"
+
+namespace quic {
+
+class QuicSimpleClientSession : public QuicSpdyClientSession {
+ public:
+ QuicSimpleClientSession(const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection,
+ const QuicServerId& server_id,
+ QuicCryptoClientConfig* crypto_config,
+ QuicClientPushPromiseIndex* push_promise_index,
+ bool drop_response_body)
+ : QuicSpdyClientSession(config,
+ supported_versions,
+ connection,
+ server_id,
+ crypto_config,
+ push_promise_index),
+ drop_response_body_(drop_response_body) {}
+
+ std::unique_ptr<QuicSpdyClientStream> CreateClientStream() override;
+
+ private:
+ const bool drop_response_body_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_SIMPLE_CLIENT_SESSION_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_stream.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_stream.cc
new file mode 100644
index 00000000000..a627007972c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_stream.cc
@@ -0,0 +1,33 @@
+// Copyright (c) 2018 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/tools/quic_simple_client_stream.h"
+
+namespace quic {
+
+void QuicSimpleClientStream::OnBodyAvailable() {
+ if (!drop_response_body_) {
+ QuicSpdyClientStream::OnBodyAvailable();
+ return;
+ }
+
+ while (HasBytesToRead()) {
+ struct iovec iov;
+ if (GetReadableRegions(&iov, 1) == 0) {
+ break;
+ }
+ MarkConsumed(iov.iov_len);
+ }
+ if (sequencer()->IsClosed()) {
+ OnFinRead();
+ } else {
+ sequencer()->SetUnblocked();
+ }
+}
+
+void QuicSimpleClientStream::OnStopSending(uint16_t code) {
+ last_stop_sending_code_ = code;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_stream.h b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_stream.h
new file mode 100644
index 00000000000..f1eb653bea4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_stream.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2018 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_TOOLS_QUIC_SIMPLE_CLIENT_STREAM_H_
+#define QUICHE_QUIC_TOOLS_QUIC_SIMPLE_CLIENT_STREAM_H_
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
+
+namespace quic {
+
+class QuicSimpleClientStream : public QuicSpdyClientStream {
+ public:
+ QuicSimpleClientStream(QuicStreamId id,
+ QuicSpdyClientSession* session,
+ StreamType type,
+ bool drop_response_body)
+ : QuicSpdyClientStream(id, session, type),
+ drop_response_body_(drop_response_body),
+ last_stop_sending_code_(0) {}
+ void OnBodyAvailable() override;
+ void OnStopSending(uint16_t code) override;
+
+ uint16_t last_stop_sending_code() { return last_stop_sending_code_; }
+ private:
+ const bool drop_response_body_;
+ // Application code value that was in the most recently received
+ // STOP_SENDING frame for this stream.
+ uint16_t last_stop_sending_code_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_SIMPLE_CLIENT_STREAM_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.cc
new file mode 100644
index 00000000000..2bb9656cb2f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.cc
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 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/tools/quic_simple_crypto_server_stream_helper.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+
+namespace quic {
+
+QuicSimpleCryptoServerStreamHelper::QuicSimpleCryptoServerStreamHelper(
+ QuicRandom* random)
+ : random_(random) {}
+
+QuicSimpleCryptoServerStreamHelper::~QuicSimpleCryptoServerStreamHelper() =
+ default;
+
+QuicConnectionId
+QuicSimpleCryptoServerStreamHelper::GenerateConnectionIdForReject(
+ QuicTransportVersion /*version*/,
+ QuicConnectionId /*connection_id*/) const {
+ return QuicUtils::CreateRandomConnectionId(random_);
+}
+
+bool QuicSimpleCryptoServerStreamHelper::CanAcceptClientHello(
+ const CryptoHandshakeMessage& message,
+ const QuicSocketAddress& client_address,
+ const QuicSocketAddress& peer_address,
+ const QuicSocketAddress& self_address,
+ std::string* error_details) const {
+ return true;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h
new file mode 100644
index 00000000000..f8a58f6f4d0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 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_TOOLS_QUIC_SIMPLE_CRYPTO_SERVER_STREAM_HELPER_H_
+#define QUICHE_QUIC_TOOLS_QUIC_SIMPLE_CRYPTO_SERVER_STREAM_HELPER_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+
+namespace quic {
+
+// Simple helper for server crypto streams which generates a new random
+// connection ID for stateless rejects.
+class QuicSimpleCryptoServerStreamHelper
+ : public QuicCryptoServerStream::Helper {
+ public:
+ explicit QuicSimpleCryptoServerStreamHelper(QuicRandom* random);
+
+ ~QuicSimpleCryptoServerStreamHelper() override;
+
+ QuicConnectionId GenerateConnectionIdForReject(
+ QuicTransportVersion /*version*/,
+ QuicConnectionId /*connection_id*/) const override;
+
+ bool CanAcceptClientHello(const CryptoHandshakeMessage& message,
+ const QuicSocketAddress& client_address,
+ const QuicSocketAddress& peer_address,
+ const QuicSocketAddress& self_address,
+ std::string* error_details) const override;
+
+ private:
+ QuicRandom* random_; // Unowned.
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_SIMPLE_CRYPTO_SERVER_STREAM_HELPER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper_test.cc
new file mode 100644
index 00000000000..4ede5889a9f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper_test.cc
@@ -0,0 +1,25 @@
+// Copyright (c) 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/tools/quic_simple_crypto_server_stream_helper.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_random.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+
+class QuicSimpleCryptoServerStreamHelperTest : public QuicTest {};
+
+TEST_F(QuicSimpleCryptoServerStreamHelperTest, GenerateConnectionIdForReject) {
+ test::MockRandom random;
+ QuicSimpleCryptoServerStreamHelper helper(&random);
+
+ EXPECT_EQ(QuicUtils::CreateRandomConnectionId(&random),
+ helper.GenerateConnectionIdForReject(QUIC_VERSION_99,
+ test::TestConnectionId()));
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.cc
new file mode 100644
index 00000000000..4706b606843
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.cc
@@ -0,0 +1,68 @@
+// Copyright (c) 2012 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/tools/quic_simple_dispatcher.h"
+
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_session.h"
+
+namespace quic {
+
+QuicSimpleDispatcher::QuicSimpleDispatcher(
+ 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,
+ QuicSimpleServerBackend* quic_simple_server_backend,
+ uint8_t expected_connection_id_length)
+ : QuicDispatcher(config,
+ crypto_config,
+ version_manager,
+ std::move(helper),
+ std::move(session_helper),
+ std::move(alarm_factory),
+ expected_connection_id_length),
+ quic_simple_server_backend_(quic_simple_server_backend) {}
+
+QuicSimpleDispatcher::~QuicSimpleDispatcher() = default;
+
+int QuicSimpleDispatcher::GetRstErrorCount(
+ QuicRstStreamErrorCode error_code) const {
+ auto it = rst_error_map_.find(error_code);
+ if (it == rst_error_map_.end()) {
+ return 0;
+ }
+ return it->second;
+}
+
+void QuicSimpleDispatcher::OnRstStreamReceived(
+ const QuicRstStreamFrame& frame) {
+ auto it = rst_error_map_.find(frame.error_code);
+ if (it == rst_error_map_.end()) {
+ rst_error_map_.insert(std::make_pair(frame.error_code, 1));
+ } else {
+ it->second++;
+ }
+}
+
+QuicServerSessionBase* QuicSimpleDispatcher::CreateQuicSession(
+ QuicConnectionId connection_id,
+ const QuicSocketAddress& client_address,
+ QuicStringPiece /*alpn*/,
+ const ParsedQuicVersion& version) {
+ // The QuicServerSessionBase 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});
+
+ QuicServerSessionBase* session = new QuicSimpleServerSession(
+ config(), GetSupportedVersions(), connection, this, session_helper(),
+ crypto_config(), compressed_certs_cache(), quic_simple_server_backend_);
+ session->Initialize();
+ return session;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.h b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.h
new file mode 100644
index 00000000000..46d976c7b5b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2012 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_TOOLS_QUIC_SIMPLE_DISPATCHER_H_
+#define QUICHE_QUIC_TOOLS_QUIC_SIMPLE_DISPATCHER_H_
+
+#include "net/third_party/quiche/src/quic/core/http/quic_server_session_base.h"
+#include "net/third_party/quiche/src/quic/core/quic_dispatcher.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h"
+
+namespace quic {
+
+class QuicSimpleDispatcher : public QuicDispatcher {
+ public:
+ QuicSimpleDispatcher(
+ 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,
+ QuicSimpleServerBackend* quic_simple_server_backend,
+ uint8_t expected_connection_id_length);
+
+ ~QuicSimpleDispatcher() override;
+
+ int GetRstErrorCount(QuicRstStreamErrorCode rst_error_code) const;
+
+ void OnRstStreamReceived(const QuicRstStreamFrame& frame) override;
+
+ protected:
+ QuicServerSessionBase* CreateQuicSession(
+ QuicConnectionId connection_id,
+ const QuicSocketAddress& client_address,
+ QuicStringPiece alpn,
+ const ParsedQuicVersion& version) override;
+
+ QuicSimpleServerBackend* server_backend() {
+ return quic_simple_server_backend_;
+ }
+
+ private:
+ QuicSimpleServerBackend* quic_simple_server_backend_; // Unowned.
+
+ // The map of the reset error code with its counter.
+ std::map<QuicRstStreamErrorCode, int> rst_error_map_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_SIMPLE_DISPATCHER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h
new file mode 100644
index 00000000000..74afb60c2d2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 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_TOOLS_QUIC_SIMPLE_SERVER_BACKEND_H_
+#define QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_BACKEND_H_
+
+#include "net/third_party/quiche/src/quic/tools/quic_backend_response.h"
+
+namespace spdy {
+class SpdyHeaderBlock;
+} // namespace spdy
+
+namespace quic {
+
+// This interface implements the functionality to fetch a response
+// from the backend (such as cache, http-proxy etc) to serve
+// requests received by a Quic Server
+class QuicSimpleServerBackend {
+ public:
+ // This interface implements the methods
+ // called by the QuicSimpleServerBackend implementation
+ // to process the request in the backend
+ class RequestHandler {
+ public:
+ virtual ~RequestHandler() {}
+
+ virtual QuicConnectionId connection_id() const = 0;
+ virtual QuicStreamId stream_id() const = 0;
+ virtual std::string peer_host() const = 0;
+ // Called when the response is ready at the backend and can be send back to
+ // the QUIC client.
+ virtual void OnResponseBackendComplete(
+ const QuicBackendResponse* response,
+ std::list<QuicBackendResponse::ServerPushInfo> resources) = 0;
+ };
+
+ virtual ~QuicSimpleServerBackend() = default;
+ // This method initializes the backend instance to fetch responses
+ // from a backend server, in-memory cache etc.
+ virtual bool InitializeBackend(const std::string& backend_url) = 0;
+ // Returns true if the backend has been successfully initialized
+ // and could be used to fetch HTTP requests
+ virtual bool IsBackendInitialized() const = 0;
+ // Triggers a HTTP request to be sent to the backend server or cache
+ // If response is immediately available, the function synchronously calls
+ // the |request_handler| with the HTTP response.
+ // If the response has to be fetched over the network, the function
+ // asynchronously calls |request_handler| with the HTTP response.
+ virtual void FetchResponseFromBackend(
+ const spdy::SpdyHeaderBlock& request_headers,
+ const std::string& request_body,
+ RequestHandler* request_handler) = 0;
+ // Clears the state of the backend instance
+ virtual void CloseBackendResponseStream(RequestHandler* request_handler) = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_BACKEND_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.cc
new file mode 100644
index 00000000000..a722d373dd7
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.cc
@@ -0,0 +1,228 @@
+// Copyright (c) 2012 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/tools/quic_simple_server_session.h"
+
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.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_ptr_util.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h"
+
+namespace quic {
+
+QuicSimpleServerSession::QuicSimpleServerSession(
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection,
+ QuicSession::Visitor* visitor,
+ QuicCryptoServerStream::Helper* helper,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicServerSessionBase(config,
+ supported_versions,
+ connection,
+ visitor,
+ helper,
+ crypto_config,
+ compressed_certs_cache),
+ highest_promised_stream_id_(
+ QuicUtils::GetInvalidStreamId(connection->transport_version())),
+ quic_simple_server_backend_(quic_simple_server_backend) {}
+
+QuicSimpleServerSession::~QuicSimpleServerSession() {
+ delete connection();
+}
+
+QuicCryptoServerStreamBase*
+QuicSimpleServerSession::CreateQuicCryptoServerStream(
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache) {
+ return new QuicCryptoServerStream(
+ crypto_config, compressed_certs_cache,
+ GetQuicReloadableFlag(enable_quic_stateless_reject_support), this,
+ stream_helper());
+}
+
+void QuicSimpleServerSession::OnStreamFrame(const QuicStreamFrame& frame) {
+ if (!IsIncomingStream(frame.stream_id)) {
+ QUIC_LOG(WARNING) << "Client shouldn't send data on server push stream";
+ connection()->CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Client sent data on server push stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+ QuicSpdySession::OnStreamFrame(frame);
+}
+
+void QuicSimpleServerSession::PromisePushResources(
+ const std::string& request_url,
+ const std::list<QuicBackendResponse::ServerPushInfo>& resources,
+ QuicStreamId original_stream_id,
+ const spdy::SpdyHeaderBlock& original_request_headers) {
+ if (!server_push_enabled()) {
+ return;
+ }
+
+ for (QuicBackendResponse::ServerPushInfo resource : resources) {
+ spdy::SpdyHeaderBlock headers = SynthesizePushRequestHeaders(
+ request_url, resource, original_request_headers);
+ highest_promised_stream_id_ +=
+ QuicUtils::StreamIdDelta(connection()->transport_version());
+ SendPushPromise(original_stream_id, highest_promised_stream_id_,
+ headers.Clone());
+ promised_streams_.push_back(PromisedStreamInfo(
+ std::move(headers), highest_promised_stream_id_, resource.priority));
+ }
+
+ // Procese promised push request as many as possible.
+ HandlePromisedPushRequests();
+}
+
+QuicSpdyStream* QuicSimpleServerSession::CreateIncomingStream(QuicStreamId id) {
+ if (!ShouldCreateIncomingStream(id)) {
+ return nullptr;
+ }
+
+ QuicSpdyStream* stream = new QuicSimpleServerStream(
+ id, this, BIDIRECTIONAL, quic_simple_server_backend_);
+ ActivateStream(QuicWrapUnique(stream));
+ return stream;
+}
+
+QuicSpdyStream* QuicSimpleServerSession::CreateIncomingStream(
+ PendingStream pending) {
+ QuicSpdyStream* stream = new QuicSimpleServerStream(
+ std::move(pending), this, BIDIRECTIONAL, quic_simple_server_backend_);
+ ActivateStream(QuicWrapUnique(stream));
+ return stream;
+}
+
+QuicSimpleServerStream*
+QuicSimpleServerSession::CreateOutgoingBidirectionalStream() {
+ DCHECK(false);
+ return nullptr;
+}
+
+QuicSimpleServerStream*
+QuicSimpleServerSession::CreateOutgoingUnidirectionalStream() {
+ if (!ShouldCreateOutgoingUnidirectionalStream()) {
+ return nullptr;
+ }
+
+ QuicSimpleServerStream* stream = new QuicSimpleServerStream(
+ GetNextOutgoingUnidirectionalStreamId(), this, WRITE_UNIDIRECTIONAL,
+ quic_simple_server_backend_);
+ ActivateStream(QuicWrapUnique(stream));
+ return stream;
+}
+
+void QuicSimpleServerSession::HandleFrameOnNonexistentOutgoingStream(
+ QuicStreamId stream_id) {
+ // If this stream is a promised but not created stream (stream_id within the
+ // range of next_outgoing_stream_id_ and highes_promised_stream_id_),
+ // connection shouldn't be closed.
+ // Otherwise behave in the same way as base class.
+ if (highest_promised_stream_id_ ==
+ QuicUtils::GetInvalidStreamId(connection()->transport_version()) ||
+ stream_id > highest_promised_stream_id_) {
+ QuicSession::HandleFrameOnNonexistentOutgoingStream(stream_id);
+ }
+}
+
+void QuicSimpleServerSession::HandleRstOnValidNonexistentStream(
+ const QuicRstStreamFrame& frame) {
+ QuicSession::HandleRstOnValidNonexistentStream(frame);
+ if (!IsClosedStream(frame.stream_id)) {
+ // If a nonexistent stream is not a closed stream and still valid, it must
+ // be a locally preserved stream. Resetting this kind of stream means
+ // cancelling the promised server push.
+ // Since PromisedStreamInfo are queued in sequence, the corresponding
+ // index for it in promised_streams_ can be calculated.
+ QuicStreamId next_stream_id = next_outgoing_unidirectional_stream_id();
+ if (connection()->transport_version() == QUIC_VERSION_99) {
+ DCHECK(!QuicUtils::IsBidirectionalStreamId(frame.stream_id));
+ }
+ DCHECK_GE(frame.stream_id, next_stream_id);
+ size_t index = (frame.stream_id - next_stream_id) /
+ QuicUtils::StreamIdDelta(connection()->transport_version());
+ DCHECK_LE(index, promised_streams_.size());
+ promised_streams_[index].is_cancelled = true;
+ control_frame_manager().WriteOrBufferRstStream(frame.stream_id,
+ QUIC_RST_ACKNOWLEDGEMENT, 0);
+ connection()->OnStreamReset(frame.stream_id, QUIC_RST_ACKNOWLEDGEMENT);
+ }
+}
+
+spdy::SpdyHeaderBlock QuicSimpleServerSession::SynthesizePushRequestHeaders(
+ std::string request_url,
+ QuicBackendResponse::ServerPushInfo resource,
+ const spdy::SpdyHeaderBlock& original_request_headers) {
+ QuicUrl push_request_url = resource.request_url;
+
+ spdy::SpdyHeaderBlock spdy_headers = original_request_headers.Clone();
+ // :authority could be different from original request.
+ spdy_headers[":authority"] = push_request_url.host();
+ spdy_headers[":path"] = push_request_url.path();
+ // Push request always use GET.
+ spdy_headers[":method"] = "GET";
+ spdy_headers["referer"] = request_url;
+ spdy_headers[":scheme"] = push_request_url.scheme();
+ // It is not possible to push a response to a request that includes a request
+ // body.
+ spdy_headers["content-length"] = "0";
+ // Remove "host" field as push request is a directly generated HTTP2 request
+ // which should use ":authority" instead of "host".
+ spdy_headers.erase("host");
+ return spdy_headers;
+}
+
+void QuicSimpleServerSession::SendPushPromise(QuicStreamId original_stream_id,
+ QuicStreamId promised_stream_id,
+ spdy::SpdyHeaderBlock headers) {
+ QUIC_DLOG(INFO) << "stream " << original_stream_id
+ << " send PUSH_PROMISE for promised stream "
+ << promised_stream_id;
+ WritePushPromise(original_stream_id, promised_stream_id, std::move(headers));
+}
+
+void QuicSimpleServerSession::HandlePromisedPushRequests() {
+ while (!promised_streams_.empty() &&
+ ShouldCreateOutgoingUnidirectionalStream()) {
+ PromisedStreamInfo& promised_info = promised_streams_.front();
+ DCHECK_EQ(next_outgoing_unidirectional_stream_id(),
+ promised_info.stream_id);
+
+ if (promised_info.is_cancelled) {
+ // This stream has been reset by client. Skip this stream id.
+ promised_streams_.pop_front();
+ GetNextOutgoingUnidirectionalStreamId();
+ return;
+ }
+
+ QuicSimpleServerStream* promised_stream =
+ static_cast<QuicSimpleServerStream*>(
+ CreateOutgoingUnidirectionalStream());
+ DCHECK_NE(promised_stream, nullptr);
+ DCHECK_EQ(promised_info.stream_id, promised_stream->id());
+ QUIC_DLOG(INFO) << "created server push stream " << promised_stream->id();
+
+ promised_stream->SetPriority(promised_info.priority);
+
+ spdy::SpdyHeaderBlock request_headers(
+ std::move(promised_info.request_headers));
+
+ promised_streams_.pop_front();
+ promised_stream->PushResponse(std::move(request_headers));
+ }
+}
+
+void QuicSimpleServerSession::OnCanCreateNewOutgoingStream() {
+ HandlePromisedPushRequests();
+}
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.h b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.h
new file mode 100644
index 00000000000..c3705342359
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.h
@@ -0,0 +1,154 @@
+// Copyright (c) 2012 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.
+
+// A toy server specific QuicSession subclass.
+
+#ifndef QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_SESSION_H_
+#define QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_SESSION_H_
+
+#include <stdint.h>
+
+#include <list>
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_server_session_base.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/tools/quic_backend_response.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h"
+
+namespace quic {
+
+namespace test {
+class QuicSimpleServerSessionPeer;
+} // namespace test
+
+class QuicSimpleServerSession : public QuicServerSessionBase {
+ public:
+ // A PromisedStreamInfo is an element of the queue to store promised
+ // stream which hasn't been created yet. It keeps a mapping between promised
+ // stream id with its priority and the headers sent out in PUSH_PROMISE.
+ struct PromisedStreamInfo {
+ public:
+ PromisedStreamInfo(spdy::SpdyHeaderBlock request_headers,
+ QuicStreamId stream_id,
+ spdy::SpdyPriority priority)
+ : request_headers(std::move(request_headers)),
+ stream_id(stream_id),
+ priority(priority),
+ is_cancelled(false) {}
+ spdy::SpdyHeaderBlock request_headers;
+ QuicStreamId stream_id;
+ spdy::SpdyPriority priority;
+ bool is_cancelled;
+ };
+
+ // Takes ownership of |connection|.
+ QuicSimpleServerSession(const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection,
+ QuicSession::Visitor* visitor,
+ QuicCryptoServerStream::Helper* helper,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicSimpleServerBackend* quic_simple_server_backend);
+ QuicSimpleServerSession(const QuicSimpleServerSession&) = delete;
+ QuicSimpleServerSession& operator=(const QuicSimpleServerSession&) = delete;
+
+ ~QuicSimpleServerSession() override;
+
+ // Override base class to detact client sending data on server push stream.
+ void OnStreamFrame(const QuicStreamFrame& frame) override;
+
+ // Send out PUSH_PROMISE for all |resources| promised stream id in each frame
+ // will increase by 2 for each item in |resources|.
+ // And enqueue HEADERS block in those PUSH_PROMISED for sending push response
+ // later.
+ virtual void PromisePushResources(
+ const std::string& request_url,
+ const std::list<QuicBackendResponse::ServerPushInfo>& resources,
+ QuicStreamId original_stream_id,
+ const spdy::SpdyHeaderBlock& original_request_headers);
+
+ void OnCanCreateNewOutgoingStream() override;
+
+ protected:
+ // QuicSession methods:
+ QuicSpdyStream* CreateIncomingStream(QuicStreamId id) override;
+ QuicSpdyStream* CreateIncomingStream(PendingStream pending) override;
+ QuicSimpleServerStream* CreateOutgoingBidirectionalStream() override;
+ QuicSimpleServerStream* CreateOutgoingUnidirectionalStream() override;
+ // Override to return true for locally preserved server push stream.
+ void HandleFrameOnNonexistentOutgoingStream(QuicStreamId stream_id) override;
+ // Override to handle reseting locally preserved streams.
+ void HandleRstOnValidNonexistentStream(
+ const QuicRstStreamFrame& frame) override;
+
+ // QuicServerSessionBaseMethod:
+ QuicCryptoServerStreamBase* CreateQuicCryptoServerStream(
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache) override;
+
+ QuicSimpleServerBackend* server_backend() {
+ return quic_simple_server_backend_;
+ }
+
+ private:
+ friend class test::QuicSimpleServerSessionPeer;
+
+ // Create a server push headers block by copying request's headers block.
+ // But replace or add these pseudo-headers as they are specific to each
+ // request:
+ // :authority, :path, :method, :scheme, referer.
+ // Copying the rest headers ensures they are the same as the original
+ // request, especially cookies.
+ spdy::SpdyHeaderBlock SynthesizePushRequestHeaders(
+ std::string request_url,
+ QuicBackendResponse::ServerPushInfo resource,
+ const spdy::SpdyHeaderBlock& original_request_headers);
+
+ // Send PUSH_PROMISE frame on headers stream.
+ void SendPushPromise(QuicStreamId original_stream_id,
+ QuicStreamId promised_stream_id,
+ spdy::SpdyHeaderBlock headers);
+
+ // Fetch response from cache for request headers enqueued into
+ // promised_headers_and_streams_ and send them on dedicated stream until
+ // reaches max_open_stream_ limit.
+ // Called when return value of GetNumOpenOutgoingStreams() changes:
+ // CloseStreamInner();
+ // StreamDraining();
+ // Note that updateFlowControlOnFinalReceivedByteOffset() won't change the
+ // return value becasue all push streams are impossible to become locally
+ // closed. Since a locally preserved stream becomes remotely closed after
+ // HandlePromisedPushRequests() starts to process it, and if it is reset
+ // locally afterwards, it will be immediately become closed and never get into
+ // locally_closed_stream_highest_offset_. So all the streams in this map
+ // are not outgoing streams.
+ void HandlePromisedPushRequests();
+
+ // Keep track of the highest stream id which has been sent in PUSH_PROMISE.
+ QuicStreamId highest_promised_stream_id_;
+
+ // Promised streams which hasn't been created yet because of max_open_stream_
+ // limit. New element is added to the end of the queue.
+ // Since outgoing stream is created in sequence, stream_id of each element in
+ // the queue also increases by 2 from previous one's. The front element's
+ // stream_id is always next_outgoing_stream_id_, and the last one is always
+ // highest_promised_stream_id_.
+ QuicDeque<PromisedStreamInfo> promised_streams_;
+
+ QuicSimpleServerBackend* quic_simple_server_backend_; // Not owned.
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_SESSION_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session_test.cc
new file mode 100644
index 00000000000..7203f128caa
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session_test.cc
@@ -0,0 +1,846 @@
+// Copyright 2013 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/tools/quic_simple_server_session.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.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"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_sustained_bandwidth_recorder_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/tools/quic_backend_response.h"
+#include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h"
+
+using testing::_;
+using testing::AtLeast;
+using testing::InSequence;
+using testing::Invoke;
+using testing::Return;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+typedef QuicSimpleServerSession::PromisedStreamInfo PromisedStreamInfo;
+} // namespace
+
+class QuicSimpleServerSessionPeer {
+ public:
+ static void SetCryptoStream(QuicSimpleServerSession* s,
+ QuicCryptoServerStream* crypto_stream) {
+ s->crypto_stream_.reset(crypto_stream);
+ s->RegisterStaticStream(
+ QuicUtils::GetCryptoStreamId(s->connection()->transport_version()),
+ crypto_stream);
+ }
+
+ static QuicSpdyStream* CreateIncomingStream(QuicSimpleServerSession* s,
+ QuicStreamId id) {
+ return s->CreateIncomingStream(id);
+ }
+
+ static QuicSimpleServerStream* CreateOutgoingUnidirectionalStream(
+ QuicSimpleServerSession* s) {
+ return s->CreateOutgoingUnidirectionalStream();
+ }
+};
+
+namespace {
+
+const size_t kMaxStreamsForTest = 10;
+
+class MockQuicCryptoServerStream : public QuicCryptoServerStream {
+ public:
+ explicit MockQuicCryptoServerStream(
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicServerSessionBase* session,
+ QuicCryptoServerStream::Helper* helper)
+ : QuicCryptoServerStream(
+ crypto_config,
+ compressed_certs_cache,
+ GetQuicReloadableFlag(
+ enable_quic_stateless_reject_support), // NOLINT
+ session,
+ helper) {}
+ MockQuicCryptoServerStream(const MockQuicCryptoServerStream&) = delete;
+ MockQuicCryptoServerStream& operator=(const MockQuicCryptoServerStream&) =
+ delete;
+ ~MockQuicCryptoServerStream() override {}
+
+ MOCK_METHOD1(SendServerConfigUpdate,
+ void(const CachedNetworkParameters* cached_network_parameters));
+
+ void set_encryption_established(bool has_established) {
+ encryption_established_override_ = has_established;
+ }
+
+ bool encryption_established() const override {
+ return QuicCryptoServerStream::encryption_established() ||
+ encryption_established_override_;
+ }
+
+ private:
+ bool encryption_established_override_ = false;
+};
+
+class MockQuicConnectionWithSendStreamData : public MockQuicConnection {
+ public:
+ MockQuicConnectionWithSendStreamData(
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective,
+ const ParsedQuicVersionVector& supported_versions)
+ : MockQuicConnection(helper,
+ alarm_factory,
+ perspective,
+ supported_versions) {
+ auto consume_all_data = [](QuicStreamId id, size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state) {
+ return QuicConsumedData(write_length, state != NO_FIN);
+ };
+ ON_CALL(*this, SendStreamData(_, _, _, _))
+ .WillByDefault(Invoke(consume_all_data));
+ }
+
+ MOCK_METHOD4(SendStreamData,
+ QuicConsumedData(QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state));
+};
+
+class MockQuicSimpleServerSession : public QuicSimpleServerSession {
+ public:
+ MockQuicSimpleServerSession(
+ const QuicConfig& config,
+ QuicConnection* connection,
+ QuicSession::Visitor* visitor,
+ QuicCryptoServerStream::Helper* helper,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicSimpleServerSession(config,
+ CurrentSupportedVersions(),
+ connection,
+ visitor,
+ helper,
+ crypto_config,
+ compressed_certs_cache,
+ quic_simple_server_backend) {}
+ // Methods taking non-copyable types like SpdyHeaderBlock by value cannot be
+ // mocked directly.
+ size_t WritePushPromise(QuicStreamId original_stream_id,
+ QuicStreamId promised_stream_id,
+ spdy::SpdyHeaderBlock headers) override {
+ return WritePushPromiseMock(original_stream_id, promised_stream_id,
+ headers);
+ }
+ MOCK_METHOD3(WritePushPromiseMock,
+ size_t(QuicStreamId original_stream_id,
+ QuicStreamId promised_stream_id,
+ const spdy::SpdyHeaderBlock& headers));
+
+ MOCK_METHOD1(SendBlocked, void(QuicStreamId));
+};
+
+class QuicSimpleServerSessionTest
+ : public QuicTestWithParam<ParsedQuicVersion> {
+ public:
+ bool ClearControlFrame(const QuicFrame& frame) {
+ DeleteFrame(&const_cast<QuicFrame&>(frame));
+ return true;
+ }
+
+ // The function ensures that A) the max stream id frames get properly deleted
+ // (since the test uses a 'did we leak memory' check ... if we just lose the
+ // frame, the test fails) and B) returns true (instead of the default, false)
+ // which ensures that the rest of the system thinks that the frame actually
+ // was transmitted.
+ bool ClearMaxStreamIdControlFrame(const QuicFrame& frame) {
+ if (frame.type == MAX_STREAM_ID_FRAME) {
+ DeleteFrame(&const_cast<QuicFrame&>(frame));
+ return true;
+ }
+ return false;
+ }
+
+ protected:
+ QuicSimpleServerSessionTest()
+ : crypto_config_(QuicCryptoServerConfig::TESTING,
+ QuicRandom::GetInstance(),
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx()),
+ compressed_certs_cache_(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize) {
+ config_.SetMaxIncomingDynamicStreamsToSend(kMaxStreamsForTest);
+ QuicConfigPeer::SetReceivedMaxIncomingDynamicStreams(&config_,
+ kMaxStreamsForTest);
+ config_.SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ config_.SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+
+ ParsedQuicVersionVector supported_versions = SupportedVersions(GetParam());
+ connection_ = new StrictMock<MockQuicConnectionWithSendStreamData>(
+ &helper_, &alarm_factory_, Perspective::IS_SERVER, supported_versions);
+ connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ session_ = QuicMakeUnique<MockQuicSimpleServerSession>(
+ config_, connection_, &owner_, &stream_helper_, &crypto_config_,
+ &compressed_certs_cache_, &memory_cache_backend_);
+ MockClock clock;
+ handshake_message_ = crypto_config_.AddDefaultConfig(
+ QuicRandom::GetInstance(), &clock,
+ QuicCryptoServerConfig::ConfigOptions());
+ session_->Initialize();
+ QuicSessionPeer::GetMutableCryptoStream(session_.get())
+ ->OnSuccessfulVersionNegotiation(supported_versions.front());
+ visitor_ = QuicConnectionPeer::GetVisitor(connection_);
+
+ if (IsVersion99()) {
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(Invoke(
+ this,
+ &QuicSimpleServerSessionTest::ClearMaxStreamIdControlFrame));
+ }
+ session_->OnConfigNegotiated();
+ }
+
+ QuicStreamId GetNthClientInitiatedBidirectionalId(int n) {
+ return GetNthClientInitiatedBidirectionalStreamId(
+ connection_->transport_version(), n);
+ }
+
+ QuicStreamId GetNthServerInitiatedUnidirectionalId(int n) {
+ return quic::test::GetNthServerInitiatedUnidirectionalStreamId(
+ connection_->transport_version(), n);
+ }
+
+ bool IsVersion99() const {
+ return connection_->transport_version() == QUIC_VERSION_99;
+ }
+
+ void InjectStopSending(QuicStreamId stream_id,
+ QuicRstStreamErrorCode rst_stream_code) {
+ // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a
+ // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes
+ // a one-way close.
+ if (!IsVersion99()) {
+ // Only needed for version 99/IETF QUIC.
+ return;
+ }
+ EXPECT_CALL(owner_, OnStopSendingReceived(_)).Times(1);
+ QuicStopSendingFrame stop_sending(
+ kInvalidControlFrameId, stream_id,
+ static_cast<QuicApplicationErrorCode>(rst_stream_code));
+ // Expect the RESET_STREAM that is generated in response to receiving a
+ // STOP_SENDING.
+ EXPECT_CALL(*connection_, OnStreamReset(stream_id, rst_stream_code));
+ session_->OnStopSendingFrame(stop_sending);
+ }
+
+ StrictMock<MockQuicSessionVisitor> owner_;
+ StrictMock<MockQuicCryptoServerStreamHelper> stream_helper_;
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ StrictMock<MockQuicConnectionWithSendStreamData>* connection_;
+ QuicConfig config_;
+ QuicCryptoServerConfig crypto_config_;
+ QuicCompressedCertsCache compressed_certs_cache_;
+ QuicMemoryCacheBackend memory_cache_backend_;
+ std::unique_ptr<MockQuicSimpleServerSession> session_;
+ std::unique_ptr<CryptoHandshakeMessage> handshake_message_;
+ QuicConnectionVisitorInterface* visitor_;
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests,
+ QuicSimpleServerSessionTest,
+ ::testing::ValuesIn(AllSupportedVersions()));
+
+TEST_P(QuicSimpleServerSessionTest, CloseStreamDueToReset) {
+ // Open a stream, then reset it.
+ // Send two bytes of payload to open it.
+ QuicStreamFrame data1(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece("HT"));
+ session_->OnStreamFrame(data1);
+ EXPECT_EQ(1u, session_->GetNumOpenIncomingStreams());
+
+ // Receive a reset (and send a RST in response).
+ QuicRstStreamFrame rst1(kInvalidControlFrameId,
+ GetNthClientInitiatedBidirectionalId(0),
+ QUIC_ERROR_PROCESSING_STREAM, 0);
+ EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1);
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ if (!IsVersion99()) {
+ // For version 99, this is covered in InjectStopSending()
+ EXPECT_CALL(*connection_,
+ OnStreamReset(GetNthClientInitiatedBidirectionalId(0),
+ QUIC_RST_ACKNOWLEDGEMENT));
+ }
+ visitor_->OnRstStream(rst1);
+ // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a
+ // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes
+ // a one-way close.
+ InjectStopSending(GetNthClientInitiatedBidirectionalId(0),
+ QUIC_ERROR_PROCESSING_STREAM);
+ EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams());
+
+ // Send the same two bytes of payload in a new packet.
+ visitor_->OnStreamFrame(data1);
+
+ // The stream should not be re-opened.
+ EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams());
+ EXPECT_TRUE(connection_->connected());
+}
+
+TEST_P(QuicSimpleServerSessionTest, NeverOpenStreamDueToReset) {
+ // Send a reset (and expect the peer to send a RST in response).
+ QuicRstStreamFrame rst1(kInvalidControlFrameId,
+ GetNthClientInitiatedBidirectionalId(0),
+ QUIC_ERROR_PROCESSING_STREAM, 0);
+ EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1);
+ if (!IsVersion99()) {
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ // For version 99, this is covered in InjectStopSending()
+ EXPECT_CALL(*connection_,
+ OnStreamReset(GetNthClientInitiatedBidirectionalId(0),
+ QUIC_RST_ACKNOWLEDGEMENT));
+ }
+ visitor_->OnRstStream(rst1);
+ // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a
+ // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes
+ // a one-way close.
+ InjectStopSending(GetNthClientInitiatedBidirectionalId(0),
+ QUIC_ERROR_PROCESSING_STREAM);
+
+ EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams());
+
+ // Send two bytes of payload.
+ QuicStreamFrame data1(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece("HT"));
+ visitor_->OnStreamFrame(data1);
+
+ // The stream should never be opened, now that the reset is received.
+ EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams());
+ EXPECT_TRUE(connection_->connected());
+}
+
+TEST_P(QuicSimpleServerSessionTest, AcceptClosedStream) {
+ // Send (empty) compressed headers followed by two bytes of data.
+ QuicStreamFrame frame1(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece("\1\0\0\0\0\0\0\0HT"));
+ QuicStreamFrame frame2(GetNthClientInitiatedBidirectionalId(1), false, 0,
+ QuicStringPiece("\2\0\0\0\0\0\0\0HT"));
+ visitor_->OnStreamFrame(frame1);
+ visitor_->OnStreamFrame(frame2);
+ EXPECT_EQ(2u, session_->GetNumOpenIncomingStreams());
+
+ // Send a reset (and expect the peer to send a RST in response).
+ QuicRstStreamFrame rst(kInvalidControlFrameId,
+ GetNthClientInitiatedBidirectionalId(0),
+ QUIC_ERROR_PROCESSING_STREAM, 0);
+ EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1);
+ if (!IsVersion99()) {
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ // For version 99, this is covered in InjectStopSending()
+ EXPECT_CALL(*connection_,
+ OnStreamReset(GetNthClientInitiatedBidirectionalId(0),
+ QUIC_RST_ACKNOWLEDGEMENT));
+ }
+ visitor_->OnRstStream(rst);
+ // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a
+ // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes
+ // a one-way close.
+ InjectStopSending(GetNthClientInitiatedBidirectionalId(0),
+ QUIC_ERROR_PROCESSING_STREAM);
+
+ // If we were tracking, we'd probably want to reject this because it's data
+ // past the reset point of stream 3. As it's a closed stream we just drop the
+ // data on the floor, but accept the packet because it has data for stream 5.
+ QuicStreamFrame frame3(GetNthClientInitiatedBidirectionalId(0), false, 2,
+ QuicStringPiece("TP"));
+ QuicStreamFrame frame4(GetNthClientInitiatedBidirectionalId(1), false, 2,
+ QuicStringPiece("TP"));
+ visitor_->OnStreamFrame(frame3);
+ visitor_->OnStreamFrame(frame4);
+ // The stream should never be opened, now that the reset is received.
+ EXPECT_EQ(1u, session_->GetNumOpenIncomingStreams());
+ EXPECT_TRUE(connection_->connected());
+}
+
+TEST_P(QuicSimpleServerSessionTest, CreateIncomingStreamDisconnected) {
+ // EXPECT_QUIC_BUG tests are expensive so only run one instance of them.
+ if (GetParam() != AllSupportedVersions()[0]) {
+ return;
+ }
+
+ // Tests that incoming stream creation fails when connection is not connected.
+ size_t initial_num_open_stream = session_->GetNumOpenIncomingStreams();
+ QuicConnectionPeer::TearDownLocalConnectionState(connection_);
+ EXPECT_QUIC_BUG(QuicSimpleServerSessionPeer::CreateIncomingStream(
+ session_.get(), GetNthClientInitiatedBidirectionalId(0)),
+ "ShouldCreateIncomingStream called when disconnected");
+ EXPECT_EQ(initial_num_open_stream, session_->GetNumOpenIncomingStreams());
+}
+
+TEST_P(QuicSimpleServerSessionTest, CreateEvenIncomingDynamicStream) {
+ // Tests that incoming stream creation fails when given stream id is even.
+ size_t initial_num_open_stream = session_->GetNumOpenIncomingStreams();
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_INVALID_STREAM_ID,
+ "Client created even numbered stream", _));
+ QuicSimpleServerSessionPeer::CreateIncomingStream(
+ session_.get(), GetNthServerInitiatedUnidirectionalId(0));
+ EXPECT_EQ(initial_num_open_stream, session_->GetNumOpenIncomingStreams());
+}
+
+TEST_P(QuicSimpleServerSessionTest, CreateIncomingStream) {
+ QuicSpdyStream* stream = QuicSimpleServerSessionPeer::CreateIncomingStream(
+ session_.get(), GetNthClientInitiatedBidirectionalId(0));
+ EXPECT_NE(nullptr, stream);
+ EXPECT_EQ(GetNthClientInitiatedBidirectionalId(0), stream->id());
+}
+
+TEST_P(QuicSimpleServerSessionTest, CreateOutgoingDynamicStreamDisconnected) {
+ // EXPECT_QUIC_BUG tests are expensive so only run one instance of them.
+ if (GetParam() != AllSupportedVersions()[0]) {
+ return;
+ }
+
+ // Tests that outgoing stream creation fails when connection is not connected.
+ size_t initial_num_open_stream = session_->GetNumOpenOutgoingStreams();
+ QuicConnectionPeer::TearDownLocalConnectionState(connection_);
+ EXPECT_QUIC_BUG(
+ QuicSimpleServerSessionPeer::CreateOutgoingUnidirectionalStream(
+ session_.get()),
+ "ShouldCreateOutgoingUnidirectionalStream called when disconnected");
+
+ EXPECT_EQ(initial_num_open_stream, session_->GetNumOpenOutgoingStreams());
+}
+
+TEST_P(QuicSimpleServerSessionTest, CreateOutgoingDynamicStreamUnencrypted) {
+ // EXPECT_QUIC_BUG tests are expensive so only run one instance of them.
+ if (GetParam() != AllSupportedVersions()[0]) {
+ return;
+ }
+
+ // Tests that outgoing stream creation fails when encryption has not yet been
+ // established.
+ size_t initial_num_open_stream = session_->GetNumOpenOutgoingStreams();
+ EXPECT_QUIC_BUG(
+ QuicSimpleServerSessionPeer::CreateOutgoingUnidirectionalStream(
+ session_.get()),
+ "Encryption not established so no outgoing stream created.");
+ EXPECT_EQ(initial_num_open_stream, session_->GetNumOpenOutgoingStreams());
+}
+
+TEST_P(QuicSimpleServerSessionTest, CreateOutgoingDynamicStreamUptoLimit) {
+ // Tests that outgoing stream creation should not be affected by existing
+ // incoming stream and vice-versa. But when reaching the limit of max outgoing
+ // stream allowed, creation should fail.
+
+ // Receive some data to initiate a incoming stream which should not effect
+ // creating outgoing streams.
+ QuicStreamFrame data1(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece("HT"));
+ session_->OnStreamFrame(data1);
+ EXPECT_EQ(1u, session_->GetNumOpenIncomingStreams());
+ EXPECT_EQ(0u, session_->GetNumOpenOutgoingStreams());
+
+ session_->UnregisterStreamPriority(
+ QuicUtils::GetHeadersStreamId(connection_->transport_version()),
+ /*is_static=*/true);
+ // Assume encryption already established.
+ QuicSimpleServerSessionPeer::SetCryptoStream(session_.get(), nullptr);
+ MockQuicCryptoServerStream* crypto_stream =
+ new MockQuicCryptoServerStream(&crypto_config_, &compressed_certs_cache_,
+ session_.get(), &stream_helper_);
+ crypto_stream->set_encryption_established(true);
+ QuicSimpleServerSessionPeer::SetCryptoStream(session_.get(), crypto_stream);
+ session_->RegisterStreamPriority(
+ QuicUtils::GetHeadersStreamId(connection_->transport_version()),
+ /*is_static=*/true, QuicStream::kDefaultPriority);
+
+ // Create push streams till reaching the upper limit of allowed open streams.
+ for (size_t i = 0; i < kMaxStreamsForTest; ++i) {
+ QuicSpdyStream* created_stream =
+ QuicSimpleServerSessionPeer::CreateOutgoingUnidirectionalStream(
+ session_.get());
+ EXPECT_EQ(GetNthServerInitiatedUnidirectionalId(i), created_stream->id());
+ EXPECT_EQ(i + 1, session_->GetNumOpenOutgoingStreams());
+ }
+
+ // Continuing creating push stream would fail.
+ EXPECT_EQ(nullptr,
+ QuicSimpleServerSessionPeer::CreateOutgoingUnidirectionalStream(
+ session_.get()));
+ EXPECT_EQ(kMaxStreamsForTest, session_->GetNumOpenOutgoingStreams());
+
+ // Create peer initiated stream should have no problem.
+ QuicStreamFrame data2(GetNthClientInitiatedBidirectionalId(1), false, 0,
+ QuicStringPiece("HT"));
+ session_->OnStreamFrame(data2);
+ EXPECT_EQ(2u, session_->GetNumOpenIncomingStreams());
+}
+
+TEST_P(QuicSimpleServerSessionTest, OnStreamFrameWithEvenStreamId) {
+ QuicStreamFrame frame(GetNthServerInitiatedUnidirectionalId(0), false, 0,
+ QuicStringPiece());
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_INVALID_STREAM_ID,
+ "Client sent data on server push stream", _));
+ session_->OnStreamFrame(frame);
+}
+
+TEST_P(QuicSimpleServerSessionTest, GetEvenIncomingError) {
+ // Tests that calling GetOrCreateDynamicStream() on an outgoing stream not
+ // promised yet should result close connection.
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID,
+ "Data for nonexistent stream", _));
+ EXPECT_EQ(nullptr,
+ QuicSessionPeer::GetOrCreateDynamicStream(
+ session_.get(), GetNthServerInitiatedUnidirectionalId(1)));
+}
+
+// In order to test the case where server push stream creation goes beyond
+// limit, server push streams need to be hanging there instead of
+// immediately closing after sending back response.
+// To achieve this goal, this class resets flow control windows so that large
+// responses will not be sent fully in order to prevent push streams from being
+// closed immediately.
+// Also adjust connection-level flow control window to ensure a large response
+// can cause stream-level flow control blocked but not connection-level.
+class QuicSimpleServerSessionServerPushTest
+ : public QuicSimpleServerSessionTest {
+ protected:
+ const size_t kStreamFlowControlWindowSize = 32 * 1024; // 32KB.
+
+ QuicSimpleServerSessionServerPushTest() {
+ // Reset stream level flow control window to be 32KB.
+ QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(
+ &config_, kStreamFlowControlWindowSize);
+ // Reset connection level flow control window to be 1.5 MB which is large
+ // enough that it won't block any stream to write before stream level flow
+ // control blocks it.
+ QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow(
+ &config_, kInitialSessionFlowControlWindowForTest);
+
+ ParsedQuicVersionVector supported_versions = SupportedVersions(GetParam());
+ connection_ = new StrictMock<MockQuicConnectionWithSendStreamData>(
+ &helper_, &alarm_factory_, Perspective::IS_SERVER, supported_versions);
+ session_ = QuicMakeUnique<MockQuicSimpleServerSession>(
+ config_, connection_, &owner_, &stream_helper_, &crypto_config_,
+ &compressed_certs_cache_, &memory_cache_backend_);
+ session_->Initialize();
+ QuicSessionPeer::GetMutableCryptoStream(session_.get())
+ ->OnSuccessfulVersionNegotiation(supported_versions.front());
+ // Needed to make new session flow control window and server push work.
+
+ if (IsVersion99()) {
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(Invoke(this, &QuicSimpleServerSessionServerPushTest::
+ ClearMaxStreamIdControlFrame));
+ }
+ session_->OnConfigNegotiated();
+
+ visitor_ = QuicConnectionPeer::GetVisitor(connection_);
+
+ session_->UnregisterStreamPriority(
+ QuicUtils::GetHeadersStreamId(connection_->transport_version()),
+ /*is_static=*/true);
+ QuicSimpleServerSessionPeer::SetCryptoStream(session_.get(), nullptr);
+ // Assume encryption already established.
+ MockQuicCryptoServerStream* crypto_stream = new MockQuicCryptoServerStream(
+ &crypto_config_, &compressed_certs_cache_, session_.get(),
+ &stream_helper_);
+
+ crypto_stream->set_encryption_established(true);
+ QuicSimpleServerSessionPeer::SetCryptoStream(session_.get(), crypto_stream);
+ session_->RegisterStreamPriority(
+ QuicUtils::GetHeadersStreamId(connection_->transport_version()),
+ /*is_static=*/true, QuicStream::kDefaultPriority);
+ }
+
+ // Given |num_resources|, create this number of fake push resources and push
+ // them by sending PUSH_PROMISE for all and sending push responses for as much
+ // as possible(limited by kMaxStreamsForTest).
+ // If |num_resources| > kMaxStreamsForTest, the left over will be queued.
+ // Returns the length of the DATA frame header, or 0 if the version does not
+ // use DATA frames.
+ QuicByteCount PromisePushResources(size_t num_resources) {
+ // testing::InSequence seq;
+ // To prevent push streams from being closed the response need to be larger
+ // than stream flow control window so stream won't send the full body.
+ size_t body_size = 2 * kStreamFlowControlWindowSize; // 64KB.
+
+ std::string request_url = "mail.google.com/";
+ spdy::SpdyHeaderBlock request_headers;
+ std::string resource_host = "www.google.com";
+ std::string partial_push_resource_path = "/server_push_src";
+ std::list<QuicBackendResponse::ServerPushInfo> push_resources;
+ std::string scheme = "http";
+ QuicByteCount data_frame_header_length = 0;
+ for (unsigned int i = 1; i <= num_resources; ++i) {
+ QuicStreamId stream_id = GetNthServerInitiatedUnidirectionalId(i - 1);
+ std::string path =
+ partial_push_resource_path + QuicTextUtils::Uint64ToString(i);
+ std::string url = scheme + "://" + resource_host + path;
+ QuicUrl resource_url = QuicUrl(url);
+ std::string body(body_size, 'a');
+ std::string data;
+ data_frame_header_length = 0;
+ if (VersionHasDataFrameHeader(connection_->transport_version())) {
+ HttpEncoder encoder;
+ std::unique_ptr<char[]> buffer;
+ data_frame_header_length =
+ encoder.SerializeDataFrameHeader(body.length(), &buffer);
+ std::string header(buffer.get(), data_frame_header_length);
+ data = header + body;
+ } else {
+ data = body;
+ }
+
+ memory_cache_backend_.AddSimpleResponse(resource_host, path, 200, data);
+ push_resources.push_back(QuicBackendResponse::ServerPushInfo(
+ resource_url, spdy::SpdyHeaderBlock(), QuicStream::kDefaultPriority,
+ body));
+ // PUSH_PROMISED are sent for all the resources.
+ EXPECT_CALL(*session_,
+ WritePushPromiseMock(GetNthClientInitiatedBidirectionalId(0),
+ stream_id, _));
+ if (i <= kMaxStreamsForTest) {
+ // |kMaxStreamsForTest| promised responses should be sent.
+ // Since flow control window is smaller than response body, not the
+ // whole body will be sent.
+ QuicStreamOffset offset = 0;
+ if (VersionHasDataFrameHeader(connection_->transport_version())) {
+ EXPECT_CALL(*connection_,
+ SendStreamData(stream_id, data_frame_header_length,
+ offset, NO_FIN));
+ offset += data_frame_header_length;
+ }
+ EXPECT_CALL(*connection_, SendStreamData(stream_id, _, offset, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(
+ kStreamFlowControlWindowSize - offset, false)));
+ EXPECT_CALL(*session_, SendBlocked(stream_id));
+ }
+ }
+ session_->PromisePushResources(request_url, push_resources,
+ GetNthClientInitiatedBidirectionalId(0),
+ request_headers);
+ return data_frame_header_length;
+ }
+
+ void ConsumeHeadersStreamData() {
+ QuicStreamId headers_stream_id =
+ QuicUtils::GetHeadersStreamId(connection_->transport_version());
+ EXPECT_CALL(*connection_, SendStreamData(headers_stream_id, _, _, _))
+ .Times(AtLeast(1));
+ }
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests,
+ QuicSimpleServerSessionServerPushTest,
+ ::testing::ValuesIn(AllSupportedVersions()));
+
+// Tests that given more than kMaxStreamsForTest resources, all their
+// PUSH_PROMISE's will be sent out and only kMaxStreamsForTest streams will be
+// opened and send push response.
+TEST_P(QuicSimpleServerSessionServerPushTest, TestPromisePushResources) {
+ ConsumeHeadersStreamData();
+ size_t num_resources = kMaxStreamsForTest + 5;
+ PromisePushResources(num_resources);
+ EXPECT_EQ(kMaxStreamsForTest, session_->GetNumOpenOutgoingStreams());
+}
+
+// Tests that after promised stream queued up, when an opened stream is marked
+// draining, a queued promised stream will become open and send push response.
+TEST_P(QuicSimpleServerSessionServerPushTest,
+ HandlePromisedPushRequestsAfterStreamDraining) {
+ ConsumeHeadersStreamData();
+ size_t num_resources = kMaxStreamsForTest + 1;
+ QuicByteCount data_frame_header_length = PromisePushResources(num_resources);
+ QuicStreamId next_out_going_stream_id =
+ GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest);
+
+ // After an open stream is marked draining, a new stream is expected to be
+ // created and a response sent on the stream.
+ QuicStreamOffset offset = 0;
+ if (VersionHasDataFrameHeader(connection_->transport_version())) {
+ EXPECT_CALL(*connection_,
+ SendStreamData(next_out_going_stream_id,
+ data_frame_header_length, offset, NO_FIN));
+ offset += data_frame_header_length;
+ }
+ EXPECT_CALL(*connection_,
+ SendStreamData(next_out_going_stream_id, _, offset, NO_FIN))
+ .WillOnce(Return(
+ QuicConsumedData(kStreamFlowControlWindowSize - offset, false)));
+ EXPECT_CALL(*session_, SendBlocked(next_out_going_stream_id));
+
+ if (IsVersion99()) {
+ // The PromisePushedResources call, above, will have used all available
+ // stream ids. For version 99, stream ids are not made available until
+ // a MAX_STREAM_ID frame is received. This emulates the reception of one.
+ // For pre-v-99, the node monitors its own stream usage and makes streams
+ // available as it closes/etc them.
+ session_->OnMaxStreamIdFrame(
+ QuicMaxStreamIdFrame(0, GetNthServerInitiatedUnidirectionalId(10)));
+ }
+ session_->StreamDraining(GetNthServerInitiatedUnidirectionalId(0));
+ // Number of open outgoing streams should still be the same, because a new
+ // stream is opened. And the queue should be empty.
+ EXPECT_EQ(kMaxStreamsForTest, session_->GetNumOpenOutgoingStreams());
+}
+
+// Tests that after all resources are promised, a RST frame from client can
+// prevent a promised resource to be send out.
+TEST_P(QuicSimpleServerSessionServerPushTest,
+ ResetPromisedStreamToCancelServerPush) {
+ ConsumeHeadersStreamData();
+
+ // Having two extra resources to be send later. One of them will be reset, so
+ // when opened stream become close, only one will become open.
+ size_t num_resources = kMaxStreamsForTest + 2;
+ if (IsVersion99()) {
+ // V99 will send out a stream-id-blocked frame when the we desired to exceed
+ // the limit. This will clear the frames so that they do not block the later
+ // rst-stream frame.
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(
+ this, &QuicSimpleServerSessionServerPushTest::ClearControlFrame));
+ }
+ QuicByteCount data_frame_header_length = PromisePushResources(num_resources);
+
+ // Reset the last stream in the queue. It should be marked cancelled.
+ QuicStreamId stream_got_reset =
+ GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest + 1);
+ QuicRstStreamFrame rst(kInvalidControlFrameId, stream_got_reset,
+ QUIC_STREAM_CANCELLED, 0);
+ EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1);
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(
+ this, &QuicSimpleServerSessionServerPushTest::ClearControlFrame));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(stream_got_reset, QUIC_RST_ACKNOWLEDGEMENT));
+ visitor_->OnRstStream(rst);
+
+ // When the first 2 streams becomes draining, the two queued up stream could
+ // be created. But since one of them was marked cancelled due to RST frame,
+ // only one queued resource will be sent out.
+ QuicStreamId stream_not_reset =
+ GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest);
+ InSequence s;
+ QuicStreamOffset offset = 0;
+ if (VersionHasDataFrameHeader(connection_->transport_version())) {
+ EXPECT_CALL(*connection_,
+ SendStreamData(stream_not_reset, data_frame_header_length,
+ offset, NO_FIN));
+ offset += data_frame_header_length;
+ }
+ EXPECT_CALL(*connection_, SendStreamData(stream_not_reset, _, offset, NO_FIN))
+ .WillOnce(Return(
+ QuicConsumedData(kStreamFlowControlWindowSize - offset, false)));
+ EXPECT_CALL(*session_, SendBlocked(stream_not_reset));
+
+ if (IsVersion99()) {
+ // The PromisePushedResources call, above, will have used all available
+ // stream ids. For version 99, stream ids are not made available until
+ // a MAX_STREAM_ID frame is received. This emulates the reception of one.
+ // For pre-v-99, the node monitors its own stream usage and makes streams
+ // available as it closes/etc them.
+ session_->OnMaxStreamIdFrame(
+ QuicMaxStreamIdFrame(0, GetNthServerInitiatedUnidirectionalId(11)));
+ }
+ session_->StreamDraining(GetNthServerInitiatedUnidirectionalId(0));
+ session_->StreamDraining(GetNthServerInitiatedUnidirectionalId(1));
+}
+
+// Tests that closing a open outgoing stream can trigger a promised resource in
+// the queue to be send out.
+TEST_P(QuicSimpleServerSessionServerPushTest,
+ CloseStreamToHandleMorePromisedStream) {
+ ConsumeHeadersStreamData();
+ size_t num_resources = kMaxStreamsForTest + 1;
+ if (IsVersion99()) {
+ // V99 will send out a stream-id-blocked frame when the we desired to exceed
+ // the limit. This will clear the frames so that they do not block the later
+ // rst-stream frame.
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(
+ this, &QuicSimpleServerSessionServerPushTest::ClearControlFrame));
+ }
+ QuicByteCount data_frame_header_length = PromisePushResources(num_resources);
+ QuicStreamId stream_to_open =
+ GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest);
+
+ // Resetting 1st open stream will close the stream and give space for extra
+ // stream to be opened.
+ QuicStreamId stream_got_reset = GetNthServerInitiatedUnidirectionalId(0);
+ EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1);
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ if (!IsVersion99()) {
+ // For version 99, this is covered in InjectStopSending()
+ EXPECT_CALL(*connection_,
+ OnStreamReset(stream_got_reset, QUIC_RST_ACKNOWLEDGEMENT));
+ }
+ QuicStreamOffset offset = 0;
+ if (VersionHasDataFrameHeader(connection_->transport_version())) {
+ EXPECT_CALL(*connection_,
+ SendStreamData(stream_to_open, data_frame_header_length, offset,
+ NO_FIN));
+ offset += data_frame_header_length;
+ }
+ EXPECT_CALL(*connection_, SendStreamData(stream_to_open, _, offset, NO_FIN))
+ .WillOnce(Return(
+ QuicConsumedData(kStreamFlowControlWindowSize - offset, false)));
+
+ EXPECT_CALL(*session_, SendBlocked(stream_to_open));
+ QuicRstStreamFrame rst(kInvalidControlFrameId, stream_got_reset,
+ QUIC_STREAM_CANCELLED, 0);
+ if (IsVersion99()) {
+ // The PromisePushedResources call, above, will have used all available
+ // stream ids. For version 99, stream ids are not made available until
+ // a MAX_STREAM_ID frame is received. This emulates the reception of one.
+ // For pre-v-99, the node monitors its own stream usage and makes streams
+ // available as it closes/etc them.
+ session_->OnMaxStreamIdFrame(
+ QuicMaxStreamIdFrame(0, GetNthServerInitiatedUnidirectionalId(10)));
+ }
+ visitor_->OnRstStream(rst);
+ // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a
+ // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes
+ // a one-way close.
+ InjectStopSending(stream_got_reset, QUIC_STREAM_CANCELLED);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.cc
new file mode 100644
index 00000000000..4e25552d03c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.cc
@@ -0,0 +1,341 @@
+// Copyright (c) 2012 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/tools/quic_simple_server_stream.h"
+
+#include <list>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.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_map_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_session.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
+
+using spdy::SpdyHeaderBlock;
+
+namespace quic {
+
+QuicSimpleServerStream::QuicSimpleServerStream(
+ QuicStreamId id,
+ QuicSpdySession* session,
+ StreamType type,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicSpdyServerStreamBase(id, session, type),
+ content_length_(-1),
+ quic_simple_server_backend_(quic_simple_server_backend) {}
+
+QuicSimpleServerStream::QuicSimpleServerStream(
+ PendingStream pending,
+ QuicSpdySession* session,
+ StreamType type,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicSpdyServerStreamBase(std::move(pending), session, type),
+ content_length_(-1),
+ quic_simple_server_backend_(quic_simple_server_backend) {}
+
+QuicSimpleServerStream::~QuicSimpleServerStream() {
+ quic_simple_server_backend_->CloseBackendResponseStream(this);
+}
+
+void QuicSimpleServerStream::OnInitialHeadersComplete(
+ bool fin,
+ size_t frame_len,
+ const QuicHeaderList& header_list) {
+ QuicSpdyStream::OnInitialHeadersComplete(fin, frame_len, header_list);
+ if (!SpdyUtils::CopyAndValidateHeaders(header_list, &content_length_,
+ &request_headers_)) {
+ QUIC_DVLOG(1) << "Invalid headers";
+ SendErrorResponse();
+ }
+ ConsumeHeaderList();
+}
+
+void QuicSimpleServerStream::OnTrailingHeadersComplete(
+ bool fin,
+ size_t frame_len,
+ const QuicHeaderList& header_list) {
+ QUIC_BUG << "Server does not support receiving Trailers.";
+ SendErrorResponse();
+}
+
+void QuicSimpleServerStream::OnBodyAvailable() {
+ while (HasBytesToRead()) {
+ struct iovec iov;
+ if (GetReadableRegions(&iov, 1) == 0) {
+ // No more data to read.
+ break;
+ }
+ QUIC_DVLOG(1) << "Stream " << id() << " processed " << iov.iov_len
+ << " bytes.";
+ body_.append(static_cast<char*>(iov.iov_base), iov.iov_len);
+
+ if (content_length_ >= 0 &&
+ body_.size() > static_cast<uint64_t>(content_length_)) {
+ QUIC_DVLOG(1) << "Body size (" << body_.size() << ") > content length ("
+ << content_length_ << ").";
+ SendErrorResponse();
+ return;
+ }
+ MarkConsumed(iov.iov_len);
+ }
+ if (!sequencer()->IsClosed()) {
+ sequencer()->SetUnblocked();
+ return;
+ }
+
+ // If the sequencer is closed, then all the body, including the fin, has been
+ // consumed.
+ OnFinRead();
+
+ if (write_side_closed() || fin_buffered()) {
+ return;
+ }
+
+ SendResponse();
+}
+
+void QuicSimpleServerStream::PushResponse(
+ SpdyHeaderBlock push_request_headers) {
+ if (QuicUtils::IsClientInitiatedStreamId(
+ session()->connection()->transport_version(), id())) {
+ QUIC_BUG << "Client initiated stream shouldn't be used as promised stream.";
+ return;
+ }
+ // Change the stream state to emulate a client request.
+ request_headers_ = std::move(push_request_headers);
+ content_length_ = 0;
+ QUIC_DVLOG(1) << "Stream " << id()
+ << " ready to receive server push response.";
+ DCHECK(reading_stopped());
+
+ // Directly send response based on the emulated request_headers_.
+ SendResponse();
+}
+
+void QuicSimpleServerStream::SendResponse() {
+ if (request_headers_.empty()) {
+ QUIC_DVLOG(1) << "Request headers empty.";
+ SendErrorResponse();
+ return;
+ }
+
+ if (content_length_ > 0 &&
+ static_cast<uint64_t>(content_length_) != body_.size()) {
+ QUIC_DVLOG(1) << "Content length (" << content_length_ << ") != body size ("
+ << body_.size() << ").";
+ SendErrorResponse();
+ return;
+ }
+
+ if (!QuicContainsKey(request_headers_, ":authority") ||
+ !QuicContainsKey(request_headers_, ":path")) {
+ QUIC_DVLOG(1) << "Request headers do not contain :authority or :path.";
+ SendErrorResponse();
+ return;
+ }
+
+ // Fetch the response from the backend interface and wait for callback once
+ // response is ready
+ quic_simple_server_backend_->FetchResponseFromBackend(request_headers_, body_,
+ this);
+}
+
+QuicConnectionId QuicSimpleServerStream::connection_id() const {
+ return spdy_session()->connection_id();
+}
+
+QuicStreamId QuicSimpleServerStream::stream_id() const {
+ return id();
+}
+
+std::string QuicSimpleServerStream::peer_host() const {
+ return spdy_session()->peer_address().host().ToString();
+}
+
+void QuicSimpleServerStream::OnResponseBackendComplete(
+ const QuicBackendResponse* response,
+ std::list<QuicBackendResponse::ServerPushInfo> resources) {
+ if (response == nullptr) {
+ QUIC_DVLOG(1) << "Response not found in cache.";
+ SendNotFoundResponse();
+ return;
+ }
+
+ if (response->response_type() == QuicBackendResponse::CLOSE_CONNECTION) {
+ QUIC_DVLOG(1) << "Special response: closing connection.";
+ CloseConnectionWithDetails(QUIC_NO_ERROR, "Toy server forcing close");
+ return;
+ }
+
+ if (response->response_type() == QuicBackendResponse::IGNORE_REQUEST) {
+ QUIC_DVLOG(1) << "Special response: ignoring request.";
+ return;
+ }
+
+ if (response->response_type() == QuicBackendResponse::BACKEND_ERR_RESPONSE) {
+ QUIC_DVLOG(1) << "Quic Proxy: Backend connection error.";
+ /*502 Bad Gateway
+ The server was acting as a gateway or proxy and received an
+ invalid response from the upstream server.*/
+ SendErrorResponse(502);
+ return;
+ }
+
+ // Examing response status, if it was not pure integer as typical h2
+ // response status, send error response. Notice that
+ // QuicHttpResponseCache push urls are strictly authority + path only,
+ // scheme is not included (see |QuicHttpResponseCache::GetKey()|).
+ std::string request_url = request_headers_[":authority"].as_string() +
+ request_headers_[":path"].as_string();
+ int response_code;
+ const SpdyHeaderBlock& response_headers = response->headers();
+ if (!ParseHeaderStatusCode(response_headers, &response_code)) {
+ auto status = response_headers.find(":status");
+ if (status == response_headers.end()) {
+ QUIC_LOG(WARNING)
+ << ":status not present in response from cache for request "
+ << request_url;
+ } else {
+ QUIC_LOG(WARNING) << "Illegal (non-integer) response :status from cache: "
+ << status->second << " for request " << request_url;
+ }
+ SendErrorResponse();
+ return;
+ }
+
+ if (QuicUtils::IsServerInitiatedStreamId(
+ session()->connection()->transport_version(), id())) {
+ // A server initiated stream is only used for a server push response,
+ // and only 200 and 30X response codes are supported for server push.
+ // This behavior mirrors the HTTP/2 implementation.
+ bool is_redirection = response_code / 100 == 3;
+ if (response_code != 200 && !is_redirection) {
+ QUIC_LOG(WARNING) << "Response to server push request " << request_url
+ << " result in response code " << response_code;
+ Reset(QUIC_STREAM_CANCELLED);
+ return;
+ }
+ }
+
+ if (!resources.empty()) {
+ QUIC_DVLOG(1) << "Stream " << id() << " found " << resources.size()
+ << " push resources.";
+ QuicSimpleServerSession* session =
+ static_cast<QuicSimpleServerSession*>(spdy_session());
+ session->PromisePushResources(request_url, resources, id(),
+ request_headers_);
+ }
+
+ if (response->response_type() == QuicBackendResponse::INCOMPLETE_RESPONSE) {
+ QUIC_DVLOG(1)
+ << "Stream " << id()
+ << " sending an incomplete response, i.e. no trailer, no fin.";
+ SendIncompleteResponse(response->headers().Clone(), response->body());
+ return;
+ }
+
+ if (response->response_type() == QuicBackendResponse::STOP_SENDING) {
+ QUIC_DVLOG(1)
+ << "Stream " << id()
+ << " sending an incomplete response, i.e. no trailer, no fin.";
+ SendIncompleteResponse(response->headers().Clone(), response->body());
+ SendStopSending(response->stop_sending_code());
+ return;
+ }
+
+ QUIC_DVLOG(1) << "Stream " << id() << " sending response.";
+ SendHeadersAndBodyAndTrailers(response->headers().Clone(), response->body(),
+ response->trailers().Clone());
+}
+
+void QuicSimpleServerStream::SendNotFoundResponse() {
+ QUIC_DVLOG(1) << "Stream " << id() << " sending not found response.";
+ SpdyHeaderBlock headers;
+ headers[":status"] = "404";
+ headers["content-length"] =
+ QuicTextUtils::Uint64ToString(strlen(kNotFoundResponseBody));
+ SendHeadersAndBody(std::move(headers), kNotFoundResponseBody);
+}
+
+void QuicSimpleServerStream::SendErrorResponse() {
+ SendErrorResponse(0);
+}
+
+void QuicSimpleServerStream::SendErrorResponse(int resp_code) {
+ QUIC_DVLOG(1) << "Stream " << id() << " sending error response.";
+ SpdyHeaderBlock headers;
+ if (resp_code <= 0) {
+ headers[":status"] = "500";
+ } else {
+ headers[":status"] = QuicTextUtils::Uint64ToString(resp_code);
+ }
+ headers["content-length"] =
+ QuicTextUtils::Uint64ToString(strlen(kErrorResponseBody));
+ SendHeadersAndBody(std::move(headers), kErrorResponseBody);
+}
+
+void QuicSimpleServerStream::SendIncompleteResponse(
+ SpdyHeaderBlock response_headers,
+ QuicStringPiece body) {
+ QUIC_DLOG(INFO) << "Stream " << id() << " writing headers (fin = false) : "
+ << response_headers.DebugString();
+ WriteHeaders(std::move(response_headers), /*fin=*/false, nullptr);
+
+ QUIC_DLOG(INFO) << "Stream " << id()
+ << " writing body (fin = false) with size: " << body.size();
+ if (!body.empty()) {
+ WriteOrBufferBody(body, /*fin=*/false);
+ }
+}
+
+void QuicSimpleServerStream::SendHeadersAndBody(
+ SpdyHeaderBlock response_headers,
+ QuicStringPiece body) {
+ SendHeadersAndBodyAndTrailers(std::move(response_headers), body,
+ SpdyHeaderBlock());
+}
+
+void QuicSimpleServerStream::SendHeadersAndBodyAndTrailers(
+ SpdyHeaderBlock response_headers,
+ QuicStringPiece body,
+ SpdyHeaderBlock response_trailers) {
+ // Send the headers, with a FIN if there's nothing else to send.
+ bool send_fin = (body.empty() && response_trailers.empty());
+ QUIC_DLOG(INFO) << "Stream " << id() << " writing headers (fin = " << send_fin
+ << ") : " << response_headers.DebugString();
+ WriteHeaders(std::move(response_headers), send_fin, nullptr);
+ if (send_fin) {
+ // Nothing else to send.
+ return;
+ }
+
+ // Send the body, with a FIN if there's no trailers to send.
+ send_fin = response_trailers.empty();
+ QUIC_DLOG(INFO) << "Stream " << id() << " writing body (fin = " << send_fin
+ << ") with size: " << body.size();
+ if (!body.empty() || send_fin) {
+ WriteOrBufferBody(body, send_fin);
+ }
+ if (send_fin) {
+ // Nothing else to send.
+ return;
+ }
+
+ // Send the trailers. A FIN is always sent with trailers.
+ QUIC_DLOG(INFO) << "Stream " << id() << " writing trailers (fin = true): "
+ << response_trailers.DebugString();
+ WriteTrailers(std::move(response_trailers), nullptr);
+}
+
+const char* const QuicSimpleServerStream::kErrorResponseBody = "bad";
+const char* const QuicSimpleServerStream::kNotFoundResponseBody =
+ "file not found";
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h
new file mode 100644
index 00000000000..e3eaa12c967
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h
@@ -0,0 +1,102 @@
+// Copyright (c) 2012 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_TOOLS_QUIC_SIMPLE_SERVER_STREAM_H_
+#define QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_STREAM_H_
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/tools/quic_backend_response.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_framer.h"
+
+namespace quic {
+
+// All this does right now is aggregate data, and on fin, send an HTTP
+// response.
+class QuicSimpleServerStream : public QuicSpdyServerStreamBase,
+ public QuicSimpleServerBackend::RequestHandler {
+ public:
+ QuicSimpleServerStream(QuicStreamId id,
+ QuicSpdySession* session,
+ StreamType type,
+ QuicSimpleServerBackend* quic_simple_server_backend);
+ QuicSimpleServerStream(PendingStream pending,
+ QuicSpdySession* session,
+ StreamType type,
+ QuicSimpleServerBackend* quic_simple_server_backend);
+ QuicSimpleServerStream(const QuicSimpleServerStream&) = delete;
+ QuicSimpleServerStream& operator=(const QuicSimpleServerStream&) = delete;
+ ~QuicSimpleServerStream() override;
+
+ // QuicSpdyStream
+ void OnInitialHeadersComplete(bool fin,
+ size_t frame_len,
+ const QuicHeaderList& header_list) override;
+ void OnTrailingHeadersComplete(bool fin,
+ size_t frame_len,
+ const QuicHeaderList& header_list) override;
+
+ // QuicStream implementation called by the sequencer when there is
+ // data (or a FIN) to be read.
+ void OnBodyAvailable() override;
+
+ // Make this stream start from as if it just finished parsing an incoming
+ // request whose headers are equivalent to |push_request_headers|.
+ // Doing so will trigger this toy stream to fetch response and send it back.
+ virtual void PushResponse(spdy::SpdyHeaderBlock push_request_headers);
+
+ // The response body of error responses.
+ static const char* const kErrorResponseBody;
+ static const char* const kNotFoundResponseBody;
+
+ // Implements QuicSimpleServerBackend::RequestHandler callbacks
+ QuicConnectionId connection_id() const override;
+ QuicStreamId stream_id() const override;
+ std::string peer_host() const override;
+ void OnResponseBackendComplete(
+ const QuicBackendResponse* response,
+ std::list<QuicBackendResponse::ServerPushInfo> resources) override;
+
+ protected:
+ // Sends a basic 200 response using SendHeaders for the headers and WriteData
+ // for the body.
+ virtual void SendResponse();
+
+ // Sends a basic 500 response using SendHeaders for the headers and WriteData
+ // for the body.
+ virtual void SendErrorResponse();
+ void SendErrorResponse(int resp_code);
+
+ // Sends a basic 404 response using SendHeaders for the headers and WriteData
+ // for the body.
+ void SendNotFoundResponse();
+
+ // Sends the response header and body, but not the fin.
+ void SendIncompleteResponse(spdy::SpdyHeaderBlock response_headers,
+ QuicStringPiece body);
+
+ void SendHeadersAndBody(spdy::SpdyHeaderBlock response_headers,
+ QuicStringPiece body);
+ void SendHeadersAndBodyAndTrailers(spdy::SpdyHeaderBlock response_headers,
+ QuicStringPiece body,
+ spdy::SpdyHeaderBlock response_trailers);
+
+ spdy::SpdyHeaderBlock* request_headers() { return &request_headers_; }
+
+ const std::string& body() { return body_; }
+
+ // The parsed headers received from the client.
+ spdy::SpdyHeaderBlock request_headers_;
+ int64_t content_length_;
+ std::string body_;
+
+ private:
+ QuicSimpleServerBackend* quic_simple_server_backend_; // Not owned.
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_STREAM_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream_test.cc
new file mode 100644
index 00000000000..7572a75da84
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream_test.cc
@@ -0,0 +1,679 @@
+// Copyright (c) 2013 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/tools/quic_simple_server_stream.h"
+
+#include <list>
+#include <memory>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/tools/quic_backend_response.h"
+#include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_session.h"
+
+using testing::_;
+using testing::AnyNumber;
+using testing::InSequence;
+using testing::Invoke;
+using testing::Return;
+using testing::StrictMock;
+using testing::ValuesIn;
+
+namespace quic {
+namespace test {
+
+const size_t kFakeFrameLen = 60;
+const size_t kErrorLength = strlen(QuicSimpleServerStream::kErrorResponseBody);
+const size_t kDataFrameHeaderLength = 2;
+
+class TestStream : public QuicSimpleServerStream {
+ public:
+ TestStream(QuicStreamId stream_id,
+ QuicSpdySession* session,
+ StreamType type,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicSimpleServerStream(stream_id,
+ session,
+ type,
+ quic_simple_server_backend) {}
+
+ ~TestStream() override = default;
+
+ MOCK_METHOD1(WriteHeadersMock, void(bool fin));
+
+ size_t WriteHeaders(spdy::SpdyHeaderBlock header_block,
+ bool fin,
+ QuicReferenceCountedPointer<QuicAckListenerInterface>
+ ack_listener) override {
+ WriteHeadersMock(fin);
+ return 0;
+ }
+
+ // Expose protected QuicSimpleServerStream methods.
+ void DoSendResponse() { SendResponse(); }
+ void DoSendErrorResponse() { SendErrorResponse(); }
+
+ spdy::SpdyHeaderBlock* mutable_headers() { return &request_headers_; }
+ void set_body(std::string body) { body_ = std::move(body); }
+ const std::string& body() const { return body_; }
+ int content_length() const { return content_length_; }
+
+ QuicStringPiece GetHeader(QuicStringPiece key) const {
+ auto it = request_headers_.find(key);
+ DCHECK(it != request_headers_.end());
+ return it->second;
+ }
+};
+
+namespace {
+
+class MockQuicSimpleServerSession : public QuicSimpleServerSession {
+ public:
+ const size_t kMaxStreamsForTest = 100;
+
+ MockQuicSimpleServerSession(
+ QuicConnection* connection,
+ MockQuicSessionVisitor* owner,
+ MockQuicCryptoServerStreamHelper* helper,
+ QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicSimpleServerSession(DefaultQuicConfig(),
+ CurrentSupportedVersions(),
+ connection,
+ owner,
+ helper,
+ crypto_config,
+ compressed_certs_cache,
+ quic_simple_server_backend) {
+ QuicSessionPeer::SetMaxOpenIncomingStreams(this, kMaxStreamsForTest);
+ QuicSessionPeer::SetMaxOpenOutgoingStreams(this, kMaxStreamsForTest);
+ ON_CALL(*this, WritevData(_, _, _, _, _))
+ .WillByDefault(Invoke(MockQuicSession::ConsumeData));
+ }
+
+ MockQuicSimpleServerSession(const MockQuicSimpleServerSession&) = delete;
+ MockQuicSimpleServerSession& operator=(const MockQuicSimpleServerSession&) =
+ delete;
+ ~MockQuicSimpleServerSession() override = default;
+
+ MOCK_METHOD3(OnConnectionClosed,
+ void(QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source));
+ MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(QuicStreamId id));
+ MOCK_METHOD5(WritevData,
+ QuicConsumedData(QuicStream* stream,
+ QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state));
+ MOCK_METHOD4(OnStreamHeaderList,
+ void(QuicStreamId stream_id,
+ bool fin,
+ size_t frame_len,
+ const QuicHeaderList& header_list));
+ MOCK_METHOD2(OnStreamHeadersPriority,
+ void(QuicStreamId stream_id, spdy::SpdyPriority priority));
+ MOCK_METHOD3(SendRstStream,
+ void(QuicStreamId stream_id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written));
+ MOCK_METHOD1(OnHeadersHeadOfLineBlocking, void(QuicTime::Delta delta));
+ // Matchers cannot be used on non-copyable types like SpdyHeaderBlock.
+ void PromisePushResources(
+ const std::string& request_url,
+ const std::list<QuicBackendResponse::ServerPushInfo>& resources,
+ QuicStreamId original_stream_id,
+ const spdy::SpdyHeaderBlock& original_request_headers) override {
+ original_request_headers_ = original_request_headers.Clone();
+ PromisePushResourcesMock(request_url, resources, original_stream_id,
+ original_request_headers);
+ }
+ MOCK_METHOD4(PromisePushResourcesMock,
+ void(const std::string&,
+ const std::list<QuicBackendResponse::ServerPushInfo>&,
+ QuicStreamId,
+ const spdy::SpdyHeaderBlock&));
+
+ using QuicSession::ActivateStream;
+
+ MOCK_METHOD1(OnStopSendingReceived, void(const QuicStopSendingFrame& frame));
+
+ spdy::SpdyHeaderBlock original_request_headers_;
+};
+
+class QuicSimpleServerStreamTest : public QuicTestWithParam<ParsedQuicVersion> {
+ public:
+ QuicSimpleServerStreamTest()
+ : connection_(
+ new StrictMock<MockQuicConnection>(&helper_,
+ &alarm_factory_,
+ Perspective::IS_SERVER,
+ SupportedVersions(GetParam()))),
+ crypto_config_(new QuicCryptoServerConfig(
+ QuicCryptoServerConfig::TESTING,
+ QuicRandom::GetInstance(),
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx())),
+ compressed_certs_cache_(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize),
+ session_(connection_,
+ &session_owner_,
+ &session_helper_,
+ crypto_config_.get(),
+ &compressed_certs_cache_,
+ &memory_cache_backend_),
+ quic_response_(new QuicBackendResponse),
+ body_("hello world") {
+ connection_->set_visitor(&session_);
+ header_list_.OnHeaderBlockStart();
+ header_list_.OnHeader(":authority", "www.google.com");
+ header_list_.OnHeader(":path", "/");
+ header_list_.OnHeader(":method", "POST");
+ header_list_.OnHeader(":version", "HTTP/1.1");
+ header_list_.OnHeader("content-length", "11");
+
+ header_list_.OnHeaderBlockEnd(128, 128);
+
+ // New streams rely on having the peer's flow control receive window
+ // negotiated in the config.
+ session_.config()->SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ session_.config()->SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ stream_ = new StrictMock<TestStream>(
+ GetNthClientInitiatedBidirectionalStreamId(
+ connection_->transport_version(), 0),
+ &session_, BIDIRECTIONAL, &memory_cache_backend_);
+ // Register stream_ in dynamic_stream_map_ and pass ownership to session_.
+ session_.ActivateStream(QuicWrapUnique(stream_));
+ connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ }
+
+ const std::string& StreamBody() { return stream_->body(); }
+
+ std::string StreamHeadersValue(const std::string& key) {
+ return (*stream_->mutable_headers())[key].as_string();
+ }
+
+ bool IsVersion99() const {
+ return connection_->transport_version() == QUIC_VERSION_99;
+ }
+
+ bool HasFrameHeader() const {
+ return VersionHasDataFrameHeader(connection_->transport_version());
+ }
+
+ spdy::SpdyHeaderBlock response_headers_;
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ StrictMock<MockQuicConnection>* connection_;
+ StrictMock<MockQuicSessionVisitor> session_owner_;
+ StrictMock<MockQuicCryptoServerStreamHelper> session_helper_;
+ std::unique_ptr<QuicCryptoServerConfig> crypto_config_;
+ QuicCompressedCertsCache compressed_certs_cache_;
+ QuicMemoryCacheBackend memory_cache_backend_;
+ StrictMock<MockQuicSimpleServerSession> session_;
+ StrictMock<TestStream>* stream_; // Owned by session_.
+ std::unique_ptr<QuicBackendResponse> quic_response_;
+ std::string body_;
+ QuicHeaderList header_list_;
+ HttpEncoder encoder_;
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests,
+ QuicSimpleServerStreamTest,
+ ValuesIn(AllSupportedVersions()));
+
+TEST_P(QuicSimpleServerStreamTest, TestFraming) {
+ EXPECT_CALL(session_, WritevData(_, _, _, _, _))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+ stream_->OnStreamHeaderList(false, kFakeFrameLen, header_list_);
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body_.length(), &buffer);
+ std::string header = std::string(buffer.get(), header_length);
+ std::string data = HasFrameHeader() ? header + body_ : body_;
+ stream_->OnStreamFrame(
+ QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
+ EXPECT_EQ("11", StreamHeadersValue("content-length"));
+ EXPECT_EQ("/", StreamHeadersValue(":path"));
+ EXPECT_EQ("POST", StreamHeadersValue(":method"));
+ EXPECT_EQ(body_, StreamBody());
+}
+
+TEST_P(QuicSimpleServerStreamTest, TestFramingOnePacket) {
+ EXPECT_CALL(session_, WritevData(_, _, _, _, _))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+
+ stream_->OnStreamHeaderList(false, kFakeFrameLen, header_list_);
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body_.length(), &buffer);
+ std::string header = std::string(buffer.get(), header_length);
+ std::string data = HasFrameHeader() ? header + body_ : body_;
+ stream_->OnStreamFrame(
+ QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
+ EXPECT_EQ("11", StreamHeadersValue("content-length"));
+ EXPECT_EQ("/", StreamHeadersValue(":path"));
+ EXPECT_EQ("POST", StreamHeadersValue(":method"));
+ EXPECT_EQ(body_, StreamBody());
+}
+
+TEST_P(QuicSimpleServerStreamTest, SendQuicRstStreamNoErrorInStopReading) {
+ EXPECT_CALL(session_, WritevData(_, _, _, _, _))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+
+ EXPECT_FALSE(stream_->fin_received());
+ EXPECT_FALSE(stream_->rst_received());
+
+ stream_->set_fin_sent(true);
+ stream_->CloseWriteSide();
+
+ EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(1);
+ stream_->StopReading();
+}
+
+TEST_P(QuicSimpleServerStreamTest, TestFramingExtraData) {
+ InSequence seq;
+ std::string large_body = "hello world!!!!!!";
+
+ // We'll automatically write out an error (headers + body)
+ EXPECT_CALL(*stream_, WriteHeadersMock(false));
+ if (HasFrameHeader()) {
+ EXPECT_CALL(session_, WritevData(_, _, kDataFrameHeaderLength, _, NO_FIN));
+ }
+ EXPECT_CALL(session_, WritevData(_, _, kErrorLength, _, FIN));
+
+ EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0);
+
+ stream_->OnStreamHeaderList(false, kFakeFrameLen, header_list_);
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body_.length(), &buffer);
+ std::string header = std::string(buffer.get(), header_length);
+ std::string data = HasFrameHeader() ? header + body_ : body_;
+
+ stream_->OnStreamFrame(
+ QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
+ // Content length is still 11. This will register as an error and we won't
+ // accept the bytes.
+ header_length =
+ encoder_.SerializeDataFrameHeader(large_body.length(), &buffer);
+ header = std::string(buffer.get(), header_length);
+ std::string data2 = HasFrameHeader() ? header + large_body : large_body;
+ stream_->OnStreamFrame(
+ QuicStreamFrame(stream_->id(), /*fin=*/true, data.size(), data2));
+ EXPECT_EQ("11", StreamHeadersValue("content-length"));
+ EXPECT_EQ("/", StreamHeadersValue(":path"));
+ EXPECT_EQ("POST", StreamHeadersValue(":method"));
+}
+
+TEST_P(QuicSimpleServerStreamTest, SendResponseWithIllegalResponseStatus) {
+ // Send an illegal response with response status not supported by HTTP/2.
+ spdy::SpdyHeaderBlock* request_headers = stream_->mutable_headers();
+ (*request_headers)[":path"] = "/bar";
+ (*request_headers)[":authority"] = "www.google.com";
+ (*request_headers)[":version"] = "HTTP/1.1";
+ (*request_headers)[":method"] = "GET";
+
+ response_headers_[":version"] = "HTTP/1.1";
+ // HTTP/2 only supports integer responsecode, so "200 OK" is illegal.
+ response_headers_[":status"] = "200 OK";
+ response_headers_["content-length"] = "5";
+ std::string body = "Yummm";
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buffer);
+
+ memory_cache_backend_.AddResponse("www.google.com", "/bar",
+ std::move(response_headers_), body);
+
+ stream_->set_fin_received(true);
+
+ InSequence s;
+ EXPECT_CALL(*stream_, WriteHeadersMock(false));
+ if (HasFrameHeader()) {
+ EXPECT_CALL(session_, WritevData(_, _, header_length, _, NO_FIN));
+ }
+ EXPECT_CALL(session_, WritevData(_, _, kErrorLength, _, FIN));
+
+ stream_->DoSendResponse();
+ EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+TEST_P(QuicSimpleServerStreamTest, SendResponseWithIllegalResponseStatus2) {
+ // Send an illegal response with response status not supported by HTTP/2.
+ spdy::SpdyHeaderBlock* request_headers = stream_->mutable_headers();
+ (*request_headers)[":path"] = "/bar";
+ (*request_headers)[":authority"] = "www.google.com";
+ (*request_headers)[":version"] = "HTTP/1.1";
+ (*request_headers)[":method"] = "GET";
+
+ response_headers_[":version"] = "HTTP/1.1";
+ // HTTP/2 only supports 3-digit-integer, so "+200" is illegal.
+ response_headers_[":status"] = "+200";
+ response_headers_["content-length"] = "5";
+ std::string body = "Yummm";
+
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buffer);
+
+ memory_cache_backend_.AddResponse("www.google.com", "/bar",
+ std::move(response_headers_), body);
+
+ stream_->set_fin_received(true);
+
+ InSequence s;
+ EXPECT_CALL(*stream_, WriteHeadersMock(false));
+ if (HasFrameHeader()) {
+ EXPECT_CALL(session_, WritevData(_, _, header_length, _, NO_FIN));
+ }
+ EXPECT_CALL(session_, WritevData(_, _, kErrorLength, _, FIN));
+
+ stream_->DoSendResponse();
+ EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+TEST_P(QuicSimpleServerStreamTest, SendPushResponseWith404Response) {
+ // Create a new promised stream with even id().
+ auto promised_stream = new StrictMock<TestStream>(
+ GetNthServerInitiatedUnidirectionalStreamId(
+ connection_->transport_version(), 0),
+ &session_, WRITE_UNIDIRECTIONAL, &memory_cache_backend_);
+ session_.ActivateStream(QuicWrapUnique(promised_stream));
+
+ // Send a push response with response status 404, which will be regarded as
+ // invalid server push response.
+ spdy::SpdyHeaderBlock* request_headers = promised_stream->mutable_headers();
+ (*request_headers)[":path"] = "/bar";
+ (*request_headers)[":authority"] = "www.google.com";
+ (*request_headers)[":version"] = "HTTP/1.1";
+ (*request_headers)[":method"] = "GET";
+
+ response_headers_[":version"] = "HTTP/1.1";
+ response_headers_[":status"] = "404";
+ response_headers_["content-length"] = "8";
+ std::string body = "NotFound";
+
+ memory_cache_backend_.AddResponse("www.google.com", "/bar",
+ std::move(response_headers_), body);
+
+ InSequence s;
+ EXPECT_CALL(session_,
+ SendRstStream(promised_stream->id(), QUIC_STREAM_CANCELLED, 0));
+
+ promised_stream->DoSendResponse();
+}
+
+TEST_P(QuicSimpleServerStreamTest, SendResponseWithValidHeaders) {
+ // Add a request and response with valid headers.
+ spdy::SpdyHeaderBlock* request_headers = stream_->mutable_headers();
+ (*request_headers)[":path"] = "/bar";
+ (*request_headers)[":authority"] = "www.google.com";
+ (*request_headers)[":version"] = "HTTP/1.1";
+ (*request_headers)[":method"] = "GET";
+
+ response_headers_[":version"] = "HTTP/1.1";
+ response_headers_[":status"] = "200";
+ response_headers_["content-length"] = "5";
+ std::string body = "Yummm";
+
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buffer);
+
+ memory_cache_backend_.AddResponse("www.google.com", "/bar",
+ std::move(response_headers_), body);
+ stream_->set_fin_received(true);
+
+ InSequence s;
+ EXPECT_CALL(*stream_, WriteHeadersMock(false));
+ if (HasFrameHeader()) {
+ EXPECT_CALL(session_, WritevData(_, _, header_length, _, NO_FIN));
+ }
+ EXPECT_CALL(session_, WritevData(_, _, body.length(), _, FIN));
+
+ stream_->DoSendResponse();
+ EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+TEST_P(QuicSimpleServerStreamTest, SendResponseWithPushResources) {
+ // Tests that if a response has push resources to be send, SendResponse() will
+ // call PromisePushResources() to handle these resources.
+
+ // Add a request and response with valid headers into cache.
+ std::string host = "www.google.com";
+ std::string request_path = "/foo";
+ std::string body = "Yummm";
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buffer);
+ QuicBackendResponse::ServerPushInfo push_info(
+ QuicUrl(host, "/bar"), spdy::SpdyHeaderBlock(),
+ QuicStream::kDefaultPriority, "Push body");
+ std::list<QuicBackendResponse::ServerPushInfo> push_resources;
+ push_resources.push_back(push_info);
+ memory_cache_backend_.AddSimpleResponseWithServerPushResources(
+ host, request_path, 200, body, push_resources);
+
+ spdy::SpdyHeaderBlock* request_headers = stream_->mutable_headers();
+ (*request_headers)[":path"] = request_path;
+ (*request_headers)[":authority"] = host;
+ (*request_headers)[":version"] = "HTTP/1.1";
+ (*request_headers)[":method"] = "GET";
+
+ stream_->set_fin_received(true);
+ InSequence s;
+ EXPECT_CALL(session_, PromisePushResourcesMock(
+ host + request_path, _,
+ GetNthClientInitiatedBidirectionalStreamId(
+ connection_->transport_version(), 0),
+ _));
+ EXPECT_CALL(*stream_, WriteHeadersMock(false));
+ if (HasFrameHeader()) {
+ EXPECT_CALL(session_, WritevData(_, _, header_length, _, NO_FIN));
+ }
+ EXPECT_CALL(session_, WritevData(_, _, body.length(), _, FIN));
+ stream_->DoSendResponse();
+ EXPECT_EQ(*request_headers, session_.original_request_headers_);
+}
+
+TEST_P(QuicSimpleServerStreamTest, PushResponseOnClientInitiatedStream) {
+ // EXPECT_QUIC_BUG tests are expensive so only run one instance of them.
+ if (GetParam() != AllSupportedVersions()[0]) {
+ return;
+ }
+
+ // Calling PushResponse() on a client initialted stream is never supposed to
+ // happen.
+ EXPECT_QUIC_BUG(stream_->PushResponse(spdy::SpdyHeaderBlock()),
+ "Client initiated stream"
+ " shouldn't be used as promised stream.");
+}
+
+TEST_P(QuicSimpleServerStreamTest, PushResponseOnServerInitiatedStream) {
+ // Tests that PushResponse() should take the given headers as request headers
+ // and fetch response from cache, and send it out.
+
+ // Create a stream with even stream id and test against this stream.
+ const QuicStreamId kServerInitiatedStreamId =
+ GetNthServerInitiatedUnidirectionalStreamId(
+ connection_->transport_version(), 0);
+ // Create a server initiated stream and pass it to session_.
+ auto server_initiated_stream =
+ new StrictMock<TestStream>(kServerInitiatedStreamId, &session_,
+ WRITE_UNIDIRECTIONAL, &memory_cache_backend_);
+ session_.ActivateStream(QuicWrapUnique(server_initiated_stream));
+
+ const std::string kHost = "www.foo.com";
+ const std::string kPath = "/bar";
+ spdy::SpdyHeaderBlock headers;
+ headers[":path"] = kPath;
+ headers[":authority"] = kHost;
+ headers[":version"] = "HTTP/1.1";
+ headers[":method"] = "GET";
+
+ response_headers_[":version"] = "HTTP/1.1";
+ response_headers_[":status"] = "200";
+ response_headers_["content-length"] = "5";
+ const std::string kBody = "Hello";
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(kBody.length(), &buffer);
+ memory_cache_backend_.AddResponse(kHost, kPath, std::move(response_headers_),
+ kBody);
+
+ // Call PushResponse() should trigger stream to fetch response from cache
+ // and send it back.
+ InSequence s;
+ EXPECT_CALL(*server_initiated_stream, WriteHeadersMock(false));
+
+ if (HasFrameHeader()) {
+ EXPECT_CALL(session_, WritevData(_, kServerInitiatedStreamId, header_length,
+ _, NO_FIN));
+ }
+ EXPECT_CALL(session_,
+ WritevData(_, kServerInitiatedStreamId, kBody.size(), _, FIN));
+ server_initiated_stream->PushResponse(std::move(headers));
+ EXPECT_EQ(kPath, server_initiated_stream->GetHeader(":path"));
+ EXPECT_EQ("GET", server_initiated_stream->GetHeader(":method"));
+}
+
+TEST_P(QuicSimpleServerStreamTest, TestSendErrorResponse) {
+ EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0);
+
+ stream_->set_fin_received(true);
+
+ InSequence s;
+ EXPECT_CALL(*stream_, WriteHeadersMock(false));
+ if (HasFrameHeader()) {
+ EXPECT_CALL(session_, WritevData(_, _, kDataFrameHeaderLength, _, NO_FIN));
+ }
+ EXPECT_CALL(session_, WritevData(_, _, kErrorLength, _, FIN));
+
+ stream_->DoSendErrorResponse();
+ EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+TEST_P(QuicSimpleServerStreamTest, InvalidMultipleContentLength) {
+ EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0);
+
+ spdy::SpdyHeaderBlock request_headers;
+ // \000 is a way to write the null byte when followed by a literal digit.
+ header_list_.OnHeader("content-length", QuicStringPiece("11\00012", 5));
+
+ EXPECT_CALL(*stream_, WriteHeadersMock(false));
+ EXPECT_CALL(session_, WritevData(_, _, _, _, _))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+ stream_->OnStreamHeaderList(true, kFakeFrameLen, header_list_);
+
+ EXPECT_TRUE(QuicStreamPeer::read_side_closed(stream_));
+ EXPECT_TRUE(stream_->reading_stopped());
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+TEST_P(QuicSimpleServerStreamTest, InvalidLeadingNullContentLength) {
+ EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0);
+
+ spdy::SpdyHeaderBlock request_headers;
+ // \000 is a way to write the null byte when followed by a literal digit.
+ header_list_.OnHeader("content-length", QuicStringPiece("\00012", 3));
+
+ EXPECT_CALL(*stream_, WriteHeadersMock(false));
+ EXPECT_CALL(session_, WritevData(_, _, _, _, _))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+ stream_->OnStreamHeaderList(true, kFakeFrameLen, header_list_);
+
+ EXPECT_TRUE(QuicStreamPeer::read_side_closed(stream_));
+ EXPECT_TRUE(stream_->reading_stopped());
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+TEST_P(QuicSimpleServerStreamTest, ValidMultipleContentLength) {
+ spdy::SpdyHeaderBlock request_headers;
+ // \000 is a way to write the null byte when followed by a literal digit.
+ header_list_.OnHeader("content-length", QuicStringPiece("11\00011", 5));
+
+ stream_->OnStreamHeaderList(false, kFakeFrameLen, header_list_);
+
+ EXPECT_EQ(11, stream_->content_length());
+ EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
+ EXPECT_FALSE(stream_->reading_stopped());
+ EXPECT_FALSE(stream_->write_side_closed());
+}
+
+TEST_P(QuicSimpleServerStreamTest,
+ DoNotSendQuicRstStreamNoErrorWithRstReceived) {
+ InSequence s;
+ EXPECT_FALSE(stream_->reading_stopped());
+
+ EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0);
+ EXPECT_CALL(session_, SendRstStream(_, QUIC_RST_ACKNOWLEDGEMENT, _)).Times(1);
+ QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(),
+ QUIC_STREAM_CANCELLED, 1234);
+ stream_->OnStreamReset(rst_frame);
+ if (IsVersion99()) {
+ // For V99 receiving a RST_STREAM causes a 1-way close; the test requires
+ // a full close. A CloseWriteSide closes the other half of the stream.
+ // Everything should then work properly.
+ stream_->CloseWriteSide();
+ }
+ EXPECT_TRUE(stream_->reading_stopped());
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+TEST_P(QuicSimpleServerStreamTest, InvalidHeadersWithFin) {
+ char arr[] = {
+ 0x3a, 0x68, 0x6f, 0x73, // :hos
+ 0x74, 0x00, 0x00, 0x00, // t...
+ 0x00, 0x00, 0x00, 0x00, // ....
+ 0x07, 0x3a, 0x6d, 0x65, // .:me
+ 0x74, 0x68, 0x6f, 0x64, // thod
+ 0x00, 0x00, 0x00, 0x03, // ....
+ 0x47, 0x45, 0x54, 0x00, // GET.
+ 0x00, 0x00, 0x05, 0x3a, // ...:
+ 0x70, 0x61, 0x74, 0x68, // path
+ 0x00, 0x00, 0x00, 0x04, // ....
+ 0x2f, 0x66, 0x6f, 0x6f, // /foo
+ 0x00, 0x00, 0x00, 0x07, // ....
+ 0x3a, 0x73, 0x63, 0x68, // :sch
+ 0x65, 0x6d, 0x65, 0x00, // eme.
+ 0x00, 0x00, 0x00, 0x00, // ....
+ 0x00, 0x00, 0x08, 0x3a, // ...:
+ 0x76, 0x65, 0x72, 0x73, // vers
+ 0x96, 0x6f, 0x6e, 0x00, // <i(69)>on.
+ 0x00, 0x00, 0x08, 0x48, // ...H
+ 0x54, 0x54, 0x50, 0x2f, // TTP/
+ 0x31, 0x2e, 0x31, // 1.1
+ };
+ QuicStringPiece data(arr, QUIC_ARRAYSIZE(arr));
+ QuicStreamFrame frame(stream_->id(), true, 0, data);
+ // Verify that we don't crash when we get a invalid headers in stream frame.
+ stream_->OnStreamFrame(frame);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.cc
new file mode 100644
index 00000000000..3d7ef028e8a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.cc
@@ -0,0 +1,273 @@
+// Copyright (c) 2015 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/tools/quic_spdy_client_base.h"
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_server_id.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_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using spdy::SpdyHeaderBlock;
+
+namespace quic {
+
+void QuicSpdyClientBase::ClientQuicDataToResend::Resend() {
+ client_->SendRequest(*headers_, body_, fin_);
+ headers_ = nullptr;
+}
+
+QuicSpdyClientBase::QuicDataToResend::QuicDataToResend(
+ std::unique_ptr<SpdyHeaderBlock> headers,
+ QuicStringPiece body,
+ bool fin)
+ : headers_(std::move(headers)), body_(body), fin_(fin) {}
+
+QuicSpdyClientBase::QuicDataToResend::~QuicDataToResend() = default;
+
+QuicSpdyClientBase::QuicSpdyClientBase(
+ const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicConfig& config,
+ QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory,
+ std::unique_ptr<NetworkHelper> network_helper,
+ std::unique_ptr<ProofVerifier> proof_verifier)
+ : QuicClientBase(server_id,
+ supported_versions,
+ config,
+ helper,
+ alarm_factory,
+ std::move(network_helper),
+ std::move(proof_verifier)),
+ store_response_(false),
+ latest_response_code_(-1) {}
+
+QuicSpdyClientBase::~QuicSpdyClientBase() {
+ // We own the push promise index. We need to explicitly kill
+ // the session before the push promise index goes out of scope.
+ ResetSession();
+}
+
+QuicSpdyClientSession* QuicSpdyClientBase::client_session() {
+ return static_cast<QuicSpdyClientSession*>(QuicClientBase::session());
+}
+
+void QuicSpdyClientBase::InitializeSession() {
+ client_session()->Initialize();
+ client_session()->CryptoConnect();
+}
+
+void QuicSpdyClientBase::OnClose(QuicSpdyStream* stream) {
+ DCHECK(stream != nullptr);
+ QuicSpdyClientStream* client_stream =
+ static_cast<QuicSpdyClientStream*>(stream);
+
+ const SpdyHeaderBlock& response_headers = client_stream->response_headers();
+ if (response_listener_ != nullptr) {
+ response_listener_->OnCompleteResponse(stream->id(), response_headers,
+ client_stream->data());
+ }
+
+ // Store response headers and body.
+ if (store_response_) {
+ auto status = response_headers.find(":status");
+ if (status == response_headers.end() ||
+ !QuicTextUtils::StringToInt(status->second, &latest_response_code_)) {
+ QUIC_LOG(ERROR) << "Invalid response headers";
+ }
+ latest_response_headers_ = response_headers.DebugString();
+ preliminary_response_headers_ =
+ client_stream->preliminary_headers().DebugString();
+ latest_response_header_block_ = response_headers.Clone();
+ latest_response_body_ = client_stream->data();
+ latest_response_trailers_ =
+ client_stream->received_trailers().DebugString();
+ }
+}
+
+std::unique_ptr<QuicSession> QuicSpdyClientBase::CreateQuicClientSession(
+ const quic::ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection) {
+ return QuicMakeUnique<QuicSpdyClientSession>(
+ *config(), supported_versions, connection, server_id(), crypto_config(),
+ &push_promise_index_);
+}
+
+void QuicSpdyClientBase::SendRequest(const SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin) {
+ QuicClientPushPromiseIndex::TryHandle* handle;
+ QuicAsyncStatus rv = push_promise_index()->Try(headers, this, &handle);
+ if (rv == QUIC_SUCCESS)
+ return;
+
+ if (rv == QUIC_PENDING) {
+ // May need to retry request if asynchronous rendezvous fails.
+ AddPromiseDataToResend(headers, body, fin);
+ return;
+ }
+
+ QuicSpdyClientStream* stream = CreateClientStream();
+ if (stream == nullptr) {
+ QUIC_BUG << "stream creation failed!";
+ return;
+ }
+ stream->SendRequest(headers.Clone(), body, fin);
+ // Record this in case we need to resend.
+ MaybeAddDataToResend(headers, body, fin);
+}
+
+void QuicSpdyClientBase::SendRequestAndWaitForResponse(
+ const SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin) {
+ SendRequest(headers, body, fin);
+ while (WaitForEvents()) {
+ }
+}
+
+void QuicSpdyClientBase::SendRequestsAndWaitForResponse(
+ const std::vector<std::string>& url_list) {
+ for (size_t i = 0; i < url_list.size(); ++i) {
+ SpdyHeaderBlock headers;
+ if (!SpdyUtils::PopulateHeaderBlockFromUrl(url_list[i], &headers)) {
+ QUIC_BUG << "Unable to create request";
+ continue;
+ }
+ SendRequest(headers, "", true);
+ }
+ while (WaitForEvents()) {
+ }
+}
+
+QuicSpdyClientStream* QuicSpdyClientBase::CreateClientStream() {
+ if (!connected()) {
+ return nullptr;
+ }
+
+ auto* stream = static_cast<QuicSpdyClientStream*>(
+ client_session()->CreateOutgoingBidirectionalStream());
+ if (stream) {
+ stream->SetPriority(QuicStream::kDefaultPriority);
+ stream->set_visitor(this);
+ }
+ return stream;
+}
+
+int QuicSpdyClientBase::GetNumSentClientHellosFromSession() {
+ return client_session()->GetNumSentClientHellos();
+}
+
+int QuicSpdyClientBase::GetNumReceivedServerConfigUpdatesFromSession() {
+ return client_session()->GetNumReceivedServerConfigUpdates();
+}
+
+void QuicSpdyClientBase::MaybeAddDataToResend(const SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin) {
+ if (!GetQuicReloadableFlag(enable_quic_stateless_reject_support)) {
+ return;
+ }
+
+ if (client_session()->IsCryptoHandshakeConfirmed()) {
+ // The handshake is confirmed. No need to continue saving requests to
+ // resend.
+ data_to_resend_on_connect_.clear();
+ return;
+ }
+
+ // The handshake is not confirmed. Push the data onto the queue of data to
+ // resend if statelessly rejected.
+ std::unique_ptr<SpdyHeaderBlock> new_headers(
+ new SpdyHeaderBlock(headers.Clone()));
+ std::unique_ptr<QuicDataToResend> data_to_resend(
+ new ClientQuicDataToResend(std::move(new_headers), body, fin, this));
+ MaybeAddQuicDataToResend(std::move(data_to_resend));
+}
+
+void QuicSpdyClientBase::MaybeAddQuicDataToResend(
+ std::unique_ptr<QuicDataToResend> data_to_resend) {
+ data_to_resend_on_connect_.push_back(std::move(data_to_resend));
+}
+
+void QuicSpdyClientBase::ClearDataToResend() {
+ data_to_resend_on_connect_.clear();
+}
+
+void QuicSpdyClientBase::ResendSavedData() {
+ // Calling Resend will re-enqueue the data, so swap out
+ // data_to_resend_on_connect_ before iterating.
+ std::vector<std::unique_ptr<QuicDataToResend>> old_data;
+ old_data.swap(data_to_resend_on_connect_);
+ for (const auto& data : old_data) {
+ data->Resend();
+ }
+}
+
+void QuicSpdyClientBase::AddPromiseDataToResend(const SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin) {
+ std::unique_ptr<SpdyHeaderBlock> new_headers(
+ new SpdyHeaderBlock(headers.Clone()));
+ push_promise_data_to_resend_.reset(
+ new ClientQuicDataToResend(std::move(new_headers), body, fin, this));
+}
+
+bool QuicSpdyClientBase::CheckVary(const SpdyHeaderBlock& client_request,
+ const SpdyHeaderBlock& promise_request,
+ const SpdyHeaderBlock& promise_response) {
+ return true;
+}
+
+void QuicSpdyClientBase::OnRendezvousResult(QuicSpdyStream* stream) {
+ std::unique_ptr<ClientQuicDataToResend> data_to_resend =
+ std::move(push_promise_data_to_resend_);
+ if (stream) {
+ stream->set_visitor(this);
+ stream->OnBodyAvailable();
+ } else if (data_to_resend) {
+ data_to_resend->Resend();
+ }
+}
+
+size_t QuicSpdyClientBase::latest_response_code() const {
+ QUIC_BUG_IF(!store_response_) << "Response not stored!";
+ return latest_response_code_;
+}
+
+const std::string& QuicSpdyClientBase::latest_response_headers() const {
+ QUIC_BUG_IF(!store_response_) << "Response not stored!";
+ return latest_response_headers_;
+}
+
+const std::string& QuicSpdyClientBase::preliminary_response_headers() const {
+ QUIC_BUG_IF(!store_response_) << "Response not stored!";
+ return preliminary_response_headers_;
+}
+
+const SpdyHeaderBlock& QuicSpdyClientBase::latest_response_header_block()
+ const {
+ QUIC_BUG_IF(!store_response_) << "Response not stored!";
+ return latest_response_header_block_;
+}
+
+const std::string& QuicSpdyClientBase::latest_response_body() const {
+ QUIC_BUG_IF(!store_response_) << "Response not stored!";
+ return latest_response_body_;
+}
+
+const std::string& QuicSpdyClientBase::latest_response_trailers() const {
+ QUIC_BUG_IF(!store_response_) << "Response not stored!";
+ return latest_response_trailers_;
+}
+
+bool QuicSpdyClientBase::HasActiveRequests() {
+ return client_session()->HasActiveRequestStreams();
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.h b/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.h
new file mode 100644
index 00000000000..03a981e1b1c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.h
@@ -0,0 +1,216 @@
+// Copyright (c) 2015 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.
+
+// A base class for the toy client, which connects to a specified port and sends
+// QUIC request to that endpoint.
+
+#ifndef QUICHE_QUIC_TOOLS_QUIC_SPDY_CLIENT_BASE_H_
+#define QUICHE_QUIC_TOOLS_QUIC_SPDY_CLIENT_BASE_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.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"
+#include "net/third_party/quiche/src/quic/tools/quic_client_base.h"
+
+namespace quic {
+
+class ProofVerifier;
+class QuicServerId;
+
+class QuicSpdyClientBase : public QuicClientBase,
+ public QuicClientPushPromiseIndex::Delegate,
+ public QuicSpdyStream::Visitor {
+ public:
+ // A ResponseListener is notified when a complete response is received.
+ class ResponseListener {
+ public:
+ ResponseListener() {}
+ virtual ~ResponseListener() {}
+ virtual void OnCompleteResponse(
+ QuicStreamId id,
+ const spdy::SpdyHeaderBlock& response_headers,
+ const std::string& response_body) = 0;
+ };
+
+ // The client uses these objects to keep track of any data to resend upon
+ // receipt of a stateless reject. Recall that the client API allows callers
+ // to optimistically send data to the server prior to handshake-confirmation.
+ // If the client subsequently receives a stateless reject, it must tear down
+ // its existing session, create a new session, and resend all previously sent
+ // data. It uses these objects to keep track of all the sent data, and to
+ // resend the data upon a subsequent connection.
+ class QuicDataToResend {
+ public:
+ // |headers| may be null, since it's possible to send data without headers.
+ QuicDataToResend(std::unique_ptr<spdy::SpdyHeaderBlock> headers,
+ QuicStringPiece body,
+ bool fin);
+ QuicDataToResend(const QuicDataToResend&) = delete;
+ QuicDataToResend& operator=(const QuicDataToResend&) = delete;
+
+ virtual ~QuicDataToResend();
+
+ // Must be overridden by specific classes with the actual method for
+ // re-sending data.
+ virtual void Resend() = 0;
+
+ protected:
+ std::unique_ptr<spdy::SpdyHeaderBlock> headers_;
+ QuicStringPiece body_;
+ bool fin_;
+ };
+
+ QuicSpdyClientBase(const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicConfig& config,
+ QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory,
+ std::unique_ptr<NetworkHelper> network_helper,
+ std::unique_ptr<ProofVerifier> proof_verifier);
+ QuicSpdyClientBase(const QuicSpdyClientBase&) = delete;
+ QuicSpdyClientBase& operator=(const QuicSpdyClientBase&) = delete;
+
+ ~QuicSpdyClientBase() override;
+
+ // QuicSpdyStream::Visitor
+ void OnClose(QuicSpdyStream* stream) override;
+
+ // A spdy session has to call CryptoConnect on top of the regular
+ // initialization.
+ void InitializeSession() override;
+
+ // Sends an HTTP request and does not wait for response before returning.
+ void SendRequest(const spdy::SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin);
+
+ // Sends an HTTP request and waits for response before returning.
+ void SendRequestAndWaitForResponse(const spdy::SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin);
+
+ // Sends a request simple GET for each URL in |url_list|, and then waits for
+ // each to complete.
+ void SendRequestsAndWaitForResponse(const std::vector<std::string>& url_list);
+
+ // Returns a newly created QuicSpdyClientStream.
+ QuicSpdyClientStream* CreateClientStream();
+
+ // Returns a the session used for this client downcasted to a
+ // QuicSpdyClientSession.
+ QuicSpdyClientSession* client_session();
+
+ QuicClientPushPromiseIndex* push_promise_index() {
+ return &push_promise_index_;
+ }
+
+ bool CheckVary(const spdy::SpdyHeaderBlock& client_request,
+ const spdy::SpdyHeaderBlock& promise_request,
+ const spdy::SpdyHeaderBlock& promise_response) override;
+ void OnRendezvousResult(QuicSpdyStream*) override;
+
+ // If the crypto handshake has not yet been confirmed, adds the data to the
+ // queue of data to resend if the client receives a stateless reject.
+ // Otherwise, deletes the data.
+ void MaybeAddQuicDataToResend(
+ std::unique_ptr<QuicDataToResend> data_to_resend);
+
+ void set_store_response(bool val) { store_response_ = val; }
+
+ size_t latest_response_code() const;
+ const std::string& latest_response_headers() const;
+ const std::string& preliminary_response_headers() const;
+ const spdy::SpdyHeaderBlock& latest_response_header_block() const;
+ const std::string& latest_response_body() const;
+ const std::string& latest_response_trailers() const;
+
+ void set_response_listener(std::unique_ptr<ResponseListener> listener) {
+ response_listener_ = std::move(listener);
+ }
+
+ protected:
+ int GetNumSentClientHellosFromSession() override;
+ int GetNumReceivedServerConfigUpdatesFromSession() override;
+
+ // Takes ownership of |connection|.
+ std::unique_ptr<QuicSession> CreateQuicClientSession(
+ const quic::ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection) override;
+
+ // If the crypto handshake has not yet been confirmed, adds the data to the
+ // queue of data to resend if the client receives a stateless reject.
+ // Otherwise, deletes the data.
+ void MaybeAddDataToResend(const spdy::SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin);
+
+ void ClearDataToResend() override;
+
+ void ResendSavedData() override;
+
+ void AddPromiseDataToResend(const spdy::SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin);
+ bool HasActiveRequests() override;
+
+ private:
+ // Specific QuicClient class for storing data to resend.
+ class ClientQuicDataToResend : public QuicDataToResend {
+ public:
+ ClientQuicDataToResend(std::unique_ptr<spdy::SpdyHeaderBlock> headers,
+ QuicStringPiece body,
+ bool fin,
+ QuicSpdyClientBase* client)
+ : QuicDataToResend(std::move(headers), body, fin), client_(client) {
+ DCHECK(headers_);
+ DCHECK(client);
+ }
+
+ ClientQuicDataToResend(const ClientQuicDataToResend&) = delete;
+ ClientQuicDataToResend& operator=(const ClientQuicDataToResend&) = delete;
+ ~ClientQuicDataToResend() override {}
+
+ void Resend() override;
+
+ private:
+ QuicSpdyClientBase* client_;
+ };
+
+ // Index of pending promised streams. Must outlive |session_|.
+ QuicClientPushPromiseIndex push_promise_index_;
+
+ // If true, store the latest response code, headers, and body.
+ bool store_response_;
+ // HTTP response code from most recent response.
+ int latest_response_code_;
+ // HTTP/2 headers from most recent response.
+ std::string latest_response_headers_;
+ // preliminary 100 Continue HTTP/2 headers from most recent response, if any.
+ std::string preliminary_response_headers_;
+ // HTTP/2 headers from most recent response.
+ spdy::SpdyHeaderBlock latest_response_header_block_;
+ // Body of most recent response.
+ std::string latest_response_body_;
+ // HTTP/2 trailers from most recent response.
+ std::string latest_response_trailers_;
+
+ // Listens for full responses.
+ std::unique_ptr<ResponseListener> response_listener_;
+
+ // Keeps track of any data that must be resent upon a subsequent successful
+ // connection, in case the client receives a stateless reject.
+ std::vector<std::unique_ptr<QuicDataToResend>> data_to_resend_on_connect_;
+
+ std::unique_ptr<ClientQuicDataToResend> push_promise_data_to_resend_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_SPDY_CLIENT_BASE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.cc
new file mode 100644
index 00000000000..63bbc5d549c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.cc
@@ -0,0 +1,111 @@
+// Copyright (c) 2018 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/tools/quic_tcp_like_trace_converter.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+
+namespace quic {
+
+QuicTcpLikeTraceConverter::QuicTcpLikeTraceConverter()
+ : largest_observed_control_frame_id_(kInvalidControlFrameId),
+ connection_offset_(0) {}
+
+QuicTcpLikeTraceConverter::StreamOffsetSegment::StreamOffsetSegment()
+ : connection_offset(0) {}
+
+QuicTcpLikeTraceConverter::StreamOffsetSegment::StreamOffsetSegment(
+ QuicStreamOffset stream_offset,
+ uint64_t connection_offset,
+ QuicByteCount data_length)
+ : stream_data(stream_offset, stream_offset + data_length),
+ connection_offset(connection_offset) {}
+
+QuicTcpLikeTraceConverter::StreamInfo::StreamInfo() : fin(false) {}
+
+QuicIntervalSet<uint64_t> QuicTcpLikeTraceConverter::OnStreamFrameSent(
+ QuicStreamId stream_id,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin) {
+ QuicIntervalSet<uint64_t> connection_offsets;
+ if (fin) {
+ // Stream fin consumes a connection offset.
+ ++data_length;
+ }
+ StreamInfo* stream_info =
+ &streams_info_.emplace(stream_id, StreamInfo()).first->second;
+ // Get connection offsets of retransmission data in this frame.
+ for (const auto& segment : stream_info->segments) {
+ QuicInterval<QuicStreamOffset> retransmission(offset, offset + data_length);
+ retransmission.IntersectWith(segment.stream_data);
+ if (retransmission.Empty()) {
+ continue;
+ }
+ const uint64_t connection_offset = segment.connection_offset +
+ retransmission.min() -
+ segment.stream_data.min();
+ connection_offsets.Add(connection_offset,
+ connection_offset + retransmission.Length());
+ }
+
+ if (stream_info->fin) {
+ return connection_offsets;
+ }
+
+ // Get connection offsets of new data in this frame.
+ QuicStreamOffset least_unsent_offset =
+ stream_info->segments.empty()
+ ? 0
+ : stream_info->segments.back().stream_data.max();
+ if (least_unsent_offset >= offset + data_length) {
+ return connection_offsets;
+ }
+ // Ignore out-of-order stream data so that as connection offset increases,
+ // stream offset increases.
+ QuicStreamOffset new_data_offset = std::max(least_unsent_offset, offset);
+ QuicByteCount new_data_length = offset + data_length - new_data_offset;
+ connection_offsets.Add(connection_offset_,
+ connection_offset_ + new_data_length);
+ if (!stream_info->segments.empty() &&
+ new_data_offset == least_unsent_offset &&
+ connection_offset_ ==
+ stream_info->segments.back().connection_offset +
+ stream_info->segments.back().stream_data.Length()) {
+ // Extend the last segment if both stream and connection offsets are
+ // contiguous.
+ stream_info->segments.back().stream_data.SetMax(new_data_offset +
+ new_data_length);
+ } else {
+ stream_info->segments.emplace_back(new_data_offset, connection_offset_,
+ new_data_length);
+ }
+ stream_info->fin = fin;
+ connection_offset_ += new_data_length;
+
+ return connection_offsets;
+}
+
+QuicInterval<uint64_t> QuicTcpLikeTraceConverter::OnControlFrameSent(
+ QuicControlFrameId control_frame_id,
+ QuicByteCount control_frame_length) {
+ if (control_frame_id > largest_observed_control_frame_id_) {
+ // New control frame.
+ QuicInterval<uint64_t> connection_offset = QuicInterval<uint64_t>(
+ connection_offset_, connection_offset_ + control_frame_length);
+ connection_offset_ += control_frame_length;
+ control_frames_info_[control_frame_id] = QuicInterval<uint64_t>(
+ connection_offset_, connection_offset_ + control_frame_length);
+ largest_observed_control_frame_id_ = control_frame_id;
+ return connection_offset;
+ }
+ const auto iter = control_frames_info_.find(control_frame_id);
+ if (iter == control_frames_info_.end()) {
+ // Ignore out of order control frames.
+ return {};
+ }
+ return iter->second;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.h b/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.h
new file mode 100644
index 00000000000..432980531be
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2018 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_TOOLS_QUIC_TCP_LIKE_TRACE_CONVERTER_H_
+#define QUICHE_QUIC_TOOLS_QUIC_TCP_LIKE_TRACE_CONVERTER_H_
+
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval_set.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+
+namespace quic {
+
+// This converter converts sent QUIC frames to connection byte offset (just like
+// TCP byte sequence number).
+class QuicTcpLikeTraceConverter {
+ public:
+ // StreamOffsetSegment stores a stream offset range which has contiguous
+ // connection offset.
+ struct StreamOffsetSegment {
+ StreamOffsetSegment();
+ StreamOffsetSegment(QuicStreamOffset stream_offset,
+ uint64_t connection_offset,
+ QuicByteCount data_length);
+
+ QuicInterval<QuicStreamOffset> stream_data;
+ uint64_t connection_offset;
+ };
+
+ QuicTcpLikeTraceConverter();
+ QuicTcpLikeTraceConverter(const QuicTcpLikeTraceConverter& other) = delete;
+ QuicTcpLikeTraceConverter(QuicTcpLikeTraceConverter&& other) = delete;
+
+ ~QuicTcpLikeTraceConverter() {}
+
+ // Called when a stream frame is sent. Returns the corresponding connection
+ // offsets.
+ QuicIntervalSet<uint64_t> OnStreamFrameSent(QuicStreamId stream_id,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin);
+
+ // Called when a control frame is sent. Returns the corresponding connection
+ // offsets.
+ QuicInterval<uint64_t> OnControlFrameSent(QuicControlFrameId control_frame_id,
+ QuicByteCount control_frame_length);
+
+ private:
+ struct StreamInfo {
+ StreamInfo();
+
+ // Stores contiguous connection offset pieces.
+ std::vector<StreamOffsetSegment> segments;
+ // Indicates whether fin has been sent.
+ bool fin;
+ };
+
+ QuicUnorderedMap<QuicStreamId, StreamInfo> streams_info_;
+ QuicUnorderedMap<QuicControlFrameId, QuicInterval<uint64_t>>
+ control_frames_info_;
+
+ QuicControlFrameId largest_observed_control_frame_id_;
+
+ uint64_t connection_offset_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_TCP_LIKE_TRACE_CONVERTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter_test.cc
new file mode 100644
index 00000000000..44f7351036b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter_test.cc
@@ -0,0 +1,103 @@
+// Copyright (c) 2018 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/tools/quic_tcp_like_trace_converter.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+TEST(QuicTcpLikeTraceConverterTest, BasicTest) {
+ QuicTcpLikeTraceConverter converter;
+
+ EXPECT_EQ(QuicIntervalSet<uint64_t>(0, 100),
+ converter.OnStreamFrameSent(1, 0, 100, false));
+ EXPECT_EQ(QuicIntervalSet<uint64_t>(100, 200),
+ converter.OnStreamFrameSent(3, 0, 100, false));
+ EXPECT_EQ(QuicIntervalSet<uint64_t>(200, 300),
+ converter.OnStreamFrameSent(3, 100, 100, false));
+ EXPECT_EQ(QuicInterval<uint64_t>(300, 450),
+ converter.OnControlFrameSent(2, 150));
+ EXPECT_EQ(QuicIntervalSet<uint64_t>(450, 550),
+ converter.OnStreamFrameSent(1, 100, 100, false));
+ EXPECT_EQ(QuicInterval<uint64_t>(550, 650),
+ converter.OnControlFrameSent(3, 100));
+ EXPECT_EQ(QuicIntervalSet<uint64_t>(650, 850),
+ converter.OnStreamFrameSent(3, 200, 200, false));
+ EXPECT_EQ(QuicInterval<uint64_t>(850, 1050),
+ converter.OnControlFrameSent(4, 200));
+ EXPECT_EQ(QuicIntervalSet<uint64_t>(1050, 1100),
+ converter.OnStreamFrameSent(1, 200, 50, false));
+ EXPECT_EQ(QuicIntervalSet<uint64_t>(1100, 1150),
+ converter.OnStreamFrameSent(1, 250, 50, false));
+ EXPECT_EQ(QuicIntervalSet<uint64_t>(1150, 1350),
+ converter.OnStreamFrameSent(3, 400, 200, false));
+
+ // Stream 1 retransmits [50, 300) and sends new data [300, 350) in the same
+ // frame.
+ QuicIntervalSet<uint64_t> expected;
+ expected.Add(50, 100);
+ expected.Add(450, 550);
+ expected.Add(1050, 1150);
+ expected.Add(1350, 1401);
+ EXPECT_EQ(expected, converter.OnStreamFrameSent(1, 50, 300, true));
+
+ expected.Clear();
+ // Stream 3 retransmits [150, 500).
+ expected.Add(250, 300);
+ expected.Add(650, 850);
+ expected.Add(1150, 1250);
+ EXPECT_EQ(expected, converter.OnStreamFrameSent(3, 150, 350, false));
+
+ // Stream 3 retransmits [300, 600) and sends new data [600, 800) in the same
+ // frame.
+ expected.Clear();
+ expected.Add(750, 850);
+ expected.Add(1150, 1350);
+ expected.Add(1401, 1602);
+ EXPECT_EQ(expected, converter.OnStreamFrameSent(3, 300, 500, true));
+
+ // Stream 3 retransmits fin only frame.
+ expected.Clear();
+ expected.Add(1601, 1602);
+ EXPECT_EQ(expected, converter.OnStreamFrameSent(3, 800, 0, true));
+
+ QuicInterval<uint64_t> expected2;
+ // Ignore out of order control frames.
+ EXPECT_EQ(expected2, converter.OnControlFrameSent(1, 100));
+
+ // Ignore passed in length for retransmitted frame.
+ expected2 = {450, 600};
+ EXPECT_EQ(expected2, converter.OnControlFrameSent(2, 200));
+
+ expected2 = {1602, 1702};
+ EXPECT_EQ(expected2, converter.OnControlFrameSent(10, 100));
+}
+
+TEST(QuicTcpLikeTraceConverterTest, FuzzerTest) {
+ QuicTcpLikeTraceConverter converter;
+ // Stream does not start from offset 0.
+ EXPECT_EQ(QuicIntervalSet<uint64_t>(0, 100),
+ converter.OnStreamFrameSent(1, 100, 100, false));
+ EXPECT_EQ(QuicIntervalSet<uint64_t>(100, 300),
+ converter.OnStreamFrameSent(3, 200, 200, false));
+ // Stream does not send data contiguously.
+ EXPECT_EQ(QuicIntervalSet<uint64_t>(300, 400),
+ converter.OnStreamFrameSent(1, 300, 100, false));
+
+ // Stream fills existing holes.
+ QuicIntervalSet<uint64_t> expected;
+ expected.Add(0, 100);
+ expected.Add(300, 501);
+ EXPECT_EQ(expected, converter.OnStreamFrameSent(1, 0, 500, true));
+
+ // Stream sends frame after fin.
+ EXPECT_EQ(expected, converter.OnStreamFrameSent(1, 50, 600, false));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_url.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_url.cc
new file mode 100644
index 00000000000..4094ffebb7e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_url.cc
@@ -0,0 +1,101 @@
+// Copyright (c) 2012 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/tools/quic_url.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+namespace quic {
+
+static constexpr size_t kMaxHostNameLength = 256;
+
+QuicUrl::QuicUrl(QuicStringPiece url) : url_(static_cast<std::string>(url)) {}
+
+QuicUrl::QuicUrl(QuicStringPiece url, QuicStringPiece default_scheme)
+ : QuicUrl(url) {
+ if (url_.has_scheme()) {
+ return;
+ }
+
+ url_ = GURL(QuicStrCat(default_scheme, "://", url));
+}
+
+std::string QuicUrl::ToString() const {
+ if (IsValid()) {
+ return url_.spec();
+ }
+ return "";
+}
+
+bool QuicUrl::IsValid() const {
+ if (!url_.is_valid() || !url_.has_scheme()) {
+ return false;
+ }
+
+ if (url_.has_host() && url_.host().length() > kMaxHostNameLength) {
+ return false;
+ }
+
+ return true;
+}
+
+std::string QuicUrl::HostPort() const {
+ if (!IsValid() || !url_.has_host()) {
+ return "";
+ }
+
+ std::string host = url_.host();
+ int port = url_.IntPort();
+ if (port == url::PORT_UNSPECIFIED) {
+ return host;
+ }
+ return QuicStrCat(host, ":", port);
+}
+
+std::string QuicUrl::PathParamsQuery() const {
+ if (!IsValid() || !url_.has_path()) {
+ return "/";
+ }
+
+ return url_.PathForRequest();
+}
+
+std::string QuicUrl::scheme() const {
+ if (!IsValid()) {
+ return "";
+ }
+
+ return url_.scheme();
+}
+
+std::string QuicUrl::host() const {
+ if (!IsValid()) {
+ return "";
+ }
+
+ return url_.HostNoBrackets();
+}
+
+std::string QuicUrl::path() const {
+ if (!IsValid()) {
+ return "";
+ }
+
+ return url_.path();
+}
+
+uint16_t QuicUrl::port() const {
+ if (!IsValid()) {
+ return 0;
+ }
+
+ int port = url_.EffectiveIntPort();
+ if (port == url::PORT_UNSPECIFIED) {
+ return 0;
+ }
+ return port;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_url.h b/chromium/net/third_party/quiche/src/quic/tools/quic_url.h
new file mode 100644
index 00000000000..da1b69db14d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_url.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2012 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_TOOLS_QUIC_URL_H_
+#define QUICHE_QUIC_TOOLS_QUIC_URL_H_
+
+#include <string>
+
+#include "url/gurl.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// A utility class that wraps GURL.
+class QuicUrl {
+ public:
+ // Constructs an empty QuicUrl.
+ QuicUrl() = default;
+
+ // Constructs a QuicUrl from the url string |url|.
+ //
+ // NOTE: If |url| doesn't have a scheme, it will have an empty scheme
+ // field. If that's not what you want, use the QuicUrlImpl(url,
+ // default_scheme) form below.
+ explicit QuicUrl(QuicStringPiece url);
+
+ // Constructs a QuicUrlImpl from |url|, assuming that the scheme for the URL
+ // is |default_scheme| if there is no scheme specified in |url|.
+ QuicUrl(QuicStringPiece url, QuicStringPiece default_scheme);
+
+ // Returns false if the URL is not valid.
+ bool IsValid() const;
+
+ // Returns full text of the QuicUrl if it is valid. Return empty string
+ // otherwise.
+ std::string ToString() const;
+
+ // Returns host:port.
+ // If the host is empty, it will return an empty string.
+ // If the host is an IPv6 address, it will be bracketed.
+ // If port is not present or is equal to default_port of scheme (e.g., port
+ // 80 for HTTP), it won't be returned.
+ std::string HostPort() const;
+
+ // Returns a string assembles path, parameters and query.
+ std::string PathParamsQuery() const;
+
+ std::string scheme() const;
+ std::string host() const;
+ std::string path() const;
+ uint16_t port() const;
+
+ private:
+ GURL url_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_URL_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_url_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_url_test.cc
new file mode 100644
index 00000000000..608fdb107fc
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_url_test.cc
@@ -0,0 +1,157 @@
+// Copyright (c) 2012 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/tools/quic_url.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+class QuicUrlTest : public QuicTest {};
+
+TEST_F(QuicUrlTest, Basic) {
+ // No scheme specified.
+ std::string url_str = "www.example.com";
+ QuicUrl url(url_str);
+ EXPECT_FALSE(url.IsValid());
+
+ // scheme is HTTP.
+ url_str = "http://www.example.com";
+ url = QuicUrl(url_str);
+ EXPECT_TRUE(url.IsValid());
+ EXPECT_EQ("http://www.example.com/", url.ToString());
+ EXPECT_EQ("http", url.scheme());
+ EXPECT_EQ("www.example.com", url.HostPort());
+ EXPECT_EQ("/", url.PathParamsQuery());
+ EXPECT_EQ(80u, url.port());
+
+ // scheme is HTTPS.
+ url_str = "https://www.example.com:12345/path/to/resource?a=1&campaign=2";
+ url = QuicUrl(url_str);
+ EXPECT_TRUE(url.IsValid());
+ EXPECT_EQ("https://www.example.com:12345/path/to/resource?a=1&campaign=2",
+ url.ToString());
+ EXPECT_EQ("https", url.scheme());
+ EXPECT_EQ("www.example.com:12345", url.HostPort());
+ EXPECT_EQ("/path/to/resource?a=1&campaign=2", url.PathParamsQuery());
+ EXPECT_EQ(12345u, url.port());
+
+ // scheme is FTP.
+ url_str = "ftp://www.example.com";
+ url = QuicUrl(url_str);
+ EXPECT_TRUE(url.IsValid());
+ EXPECT_EQ("ftp://www.example.com/", url.ToString());
+ EXPECT_EQ("ftp", url.scheme());
+ EXPECT_EQ("www.example.com", url.HostPort());
+ EXPECT_EQ("/", url.PathParamsQuery());
+ EXPECT_EQ(21u, url.port());
+}
+
+TEST_F(QuicUrlTest, DefaultScheme) {
+ // Default scheme to HTTP.
+ std::string url_str = "www.example.com";
+ QuicUrl url(url_str, "http");
+ EXPECT_EQ("http://www.example.com/", url.ToString());
+ EXPECT_EQ("http", url.scheme());
+
+ // URL already has a scheme specified.
+ url_str = "http://www.example.com";
+ url = QuicUrl(url_str, "https");
+ EXPECT_EQ("http://www.example.com/", url.ToString());
+ EXPECT_EQ("http", url.scheme());
+
+ // Default scheme to FTP.
+ url_str = "www.example.com";
+ url = QuicUrl(url_str, "ftp");
+ EXPECT_EQ("ftp://www.example.com/", url.ToString());
+ EXPECT_EQ("ftp", url.scheme());
+}
+
+TEST_F(QuicUrlTest, IsValid) {
+ std::string url_str =
+ "ftp://www.example.com:12345/path/to/resource?a=1&campaign=2";
+ EXPECT_TRUE(QuicUrl(url_str).IsValid());
+
+ // Invalid characters in host name.
+ url_str = "https://www%.example.com:12345/path/to/resource?a=1&campaign=2";
+ EXPECT_FALSE(QuicUrl(url_str).IsValid());
+
+ // Invalid characters in scheme.
+ url_str = "%http://www.example.com:12345/path/to/resource?a=1&campaign=2";
+ EXPECT_FALSE(QuicUrl(url_str).IsValid());
+
+ // Host name too long.
+ std::string host(1024, 'a');
+ url_str = "https://" + host;
+ EXPECT_FALSE(QuicUrl(url_str).IsValid());
+
+ // Invalid port number.
+ url_str = "https://www..example.com:123456/path/to/resource?a=1&campaign=2";
+ EXPECT_FALSE(QuicUrl(url_str).IsValid());
+}
+
+TEST_F(QuicUrlTest, HostPort) {
+ std::string url_str = "http://www.example.com/";
+ QuicUrl url(url_str);
+ EXPECT_EQ("www.example.com", url.HostPort());
+ EXPECT_EQ("www.example.com", url.host());
+ EXPECT_EQ(80u, url.port());
+
+ url_str = "http://www.example.com:80/";
+ url = QuicUrl(url_str);
+ EXPECT_EQ("www.example.com", url.HostPort());
+ EXPECT_EQ("www.example.com", url.host());
+ EXPECT_EQ(80u, url.port());
+
+ url_str = "http://www.example.com:81/";
+ url = QuicUrl(url_str);
+ EXPECT_EQ("www.example.com:81", url.HostPort());
+ EXPECT_EQ("www.example.com", url.host());
+ EXPECT_EQ(81u, url.port());
+
+ url_str = "https://192.168.1.1:443/";
+ url = QuicUrl(url_str);
+ EXPECT_EQ("192.168.1.1", url.HostPort());
+ EXPECT_EQ("192.168.1.1", url.host());
+ EXPECT_EQ(443u, url.port());
+
+ url_str = "http://[2001::1]:80/";
+ url = QuicUrl(url_str);
+ EXPECT_EQ("[2001::1]", url.HostPort());
+ EXPECT_EQ("2001::1", url.host());
+ EXPECT_EQ(80u, url.port());
+
+ url_str = "http://[2001::1]:81/";
+ url = QuicUrl(url_str);
+ EXPECT_EQ("[2001::1]:81", url.HostPort());
+ EXPECT_EQ("2001::1", url.host());
+ EXPECT_EQ(81u, url.port());
+}
+
+TEST_F(QuicUrlTest, PathParamsQuery) {
+ std::string url_str =
+ "https://www.example.com:12345/path/to/resource?a=1&campaign=2";
+ QuicUrl url(url_str);
+ EXPECT_EQ("/path/to/resource?a=1&campaign=2", url.PathParamsQuery());
+ EXPECT_EQ("/path/to/resource", url.path());
+
+ url_str = "https://www.example.com/?";
+ url = QuicUrl(url_str);
+ EXPECT_EQ("/?", url.PathParamsQuery());
+ EXPECT_EQ("/", url.path());
+
+ url_str = "https://www.example.com/";
+ url = QuicUrl(url_str);
+ EXPECT_EQ("/", url.PathParamsQuery());
+ EXPECT_EQ("/", url.path());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic