diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-05-24 11:40:17 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-05-24 12:42:11 +0000 |
commit | 5d87695f37678f96492b258bbab36486c59866b4 (patch) | |
tree | be9783bbaf04fb930c4d74ca9c00b5e7954c8bc6 /chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc | |
parent | 6c11fb357ec39bf087b8b632e2b1e375aef1b38b (diff) | |
download | qtwebengine-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.cc | 353 |
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 |