summaryrefslogtreecommitdiff
path: root/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2019-05-24 11:40:17 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2019-05-24 12:42:11 +0000
commit5d87695f37678f96492b258bbab36486c59866b4 (patch)
treebe9783bbaf04fb930c4d74ca9c00b5e7954c8bc6 /chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc
parent6c11fb357ec39bf087b8b632e2b1e375aef1b38b (diff)
downloadqtwebengine-chromium-5d87695f37678f96492b258bbab36486c59866b4.tar.gz
BASELINE: Update Chromium to 75.0.3770.56
Change-Id: I86d2007fd27a45d5797eee06f4c9369b8b50ac4f Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc')
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc353
1 files changed, 353 insertions, 0 deletions
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