| // 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/quic/quic_chromium_client_session.h" |
| |
| #include <utility> |
| |
| #include "base/callback_helpers.h" |
| #include "base/location.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/metrics/sparse_histogram.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/values.h" |
| #include "net/base/io_buffer.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/network_activity_monitor.h" |
| #include "net/http/http_log_util.h" |
| #include "net/http/transport_security_state.h" |
| #include "net/quic/crypto/proof_verifier_chromium.h" |
| #include "net/quic/crypto/quic_server_info.h" |
| #include "net/quic/quic_chromium_connection_helper.h" |
| #include "net/quic/quic_client_promised_info.h" |
| #include "net/quic/quic_crypto_client_stream_factory.h" |
| #include "net/quic/quic_stream_factory.h" |
| #include "net/spdy/spdy_session.h" |
| #include "net/ssl/channel_id_service.h" |
| #include "net/ssl/ssl_connection_status_flags.h" |
| #include "net/ssl/ssl_info.h" |
| #include "net/ssl/token_binding.h" |
| #include "net/udp/datagram_client_socket.h" |
| |
| namespace net { |
| |
| namespace { |
| |
| // The length of time to wait for a 0-RTT handshake to complete |
| // before allowing the requests to possibly proceed over TCP. |
| const int k0RttHandshakeTimeoutMs = 300; |
| |
| // IPv6 packets have an additional 20 bytes of overhead than IPv4 packets. |
| const size_t kAdditionalOverheadForIPv6 = 20; |
| |
| // Maximum number of Readers that are created for any session due to |
| // connection migration. A new Reader is created every time this endpoint's |
| // IP address changes. |
| const size_t kMaxReadersPerQuicSession = 5; |
| |
| // Size of the MRU cache of Token Binding signatures. Since the material being |
| // signed is constant and there aren't many keys being used to sign, a fairly |
| // small number was chosen, somewhat arbitrarily, and to match |
| // SSLClientSocketImpl. |
| const size_t kTokenBindingSignatureMapSize = 10; |
| |
| // Histograms for tracking down the crashes from http://crbug.com/354669 |
| // Note: these values must be kept in sync with the corresponding values in: |
| // tools/metrics/histograms/histograms.xml |
| enum Location { |
| DESTRUCTOR = 0, |
| ADD_OBSERVER = 1, |
| TRY_CREATE_STREAM = 2, |
| CREATE_OUTGOING_RELIABLE_STREAM = 3, |
| NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER = 4, |
| NOTIFY_FACTORY_OF_SESSION_CLOSED = 5, |
| NUM_LOCATIONS = 6, |
| }; |
| |
| void RecordUnexpectedOpenStreams(Location location) { |
| UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedOpenStreams", location, |
| NUM_LOCATIONS); |
| } |
| |
| void RecordUnexpectedObservers(Location location) { |
| UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedObservers", location, |
| NUM_LOCATIONS); |
| } |
| |
| void RecordUnexpectedNotGoingAway(Location location) { |
| UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedNotGoingAway", location, |
| NUM_LOCATIONS); |
| } |
| |
| // Histogram for recording the different reasons that a QUIC session is unable |
| // to complete the handshake. |
| enum HandshakeFailureReason { |
| HANDSHAKE_FAILURE_UNKNOWN = 0, |
| HANDSHAKE_FAILURE_BLACK_HOLE = 1, |
| HANDSHAKE_FAILURE_PUBLIC_RESET = 2, |
| NUM_HANDSHAKE_FAILURE_REASONS = 3, |
| }; |
| |
| void RecordHandshakeFailureReason(HandshakeFailureReason reason) { |
| UMA_HISTOGRAM_ENUMERATION( |
| "Net.QuicSession.ConnectionClose.HandshakeNotConfirmed.Reason", reason, |
| NUM_HANDSHAKE_FAILURE_REASONS); |
| } |
| |
| // Note: these values must be kept in sync with the corresponding values in: |
| // tools/metrics/histograms/histograms.xml |
| enum HandshakeState { |
| STATE_STARTED = 0, |
| STATE_ENCRYPTION_ESTABLISHED = 1, |
| STATE_HANDSHAKE_CONFIRMED = 2, |
| STATE_FAILED = 3, |
| NUM_HANDSHAKE_STATES = 4 |
| }; |
| |
| void RecordHandshakeState(HandshakeState state) { |
| UMA_HISTOGRAM_ENUMERATION("Net.QuicHandshakeState", state, |
| NUM_HANDSHAKE_STATES); |
| } |
| |
| std::unique_ptr<base::Value> NetLogQuicClientSessionCallback( |
| const QuicServerId* server_id, |
| int cert_verify_flags, |
| bool require_confirmation, |
| NetLogCaptureMode /* capture_mode */) { |
| std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); |
| dict->SetString("host", server_id->host()); |
| dict->SetInteger("port", server_id->port()); |
| dict->SetBoolean("privacy_mode", |
| server_id->privacy_mode() == PRIVACY_MODE_ENABLED); |
| dict->SetBoolean("require_confirmation", require_confirmation); |
| dict->SetInteger("cert_verify_flags", cert_verify_flags); |
| return std::move(dict); |
| } |
| |
| std::unique_ptr<base::Value> NetLogQuicPushPromiseReceivedCallback( |
| const SpdyHeaderBlock* headers, |
| SpdyStreamId stream_id, |
| SpdyStreamId promised_stream_id, |
| NetLogCaptureMode capture_mode) { |
| std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); |
| dict->Set("headers", ElideSpdyHeaderBlockForNetLog(*headers, capture_mode)); |
| dict->SetInteger("id", stream_id); |
| dict->SetInteger("promised_stream_id", promised_stream_id); |
| return std::move(dict); |
| } |
| |
| class HpackEncoderDebugVisitor : public QuicHeadersStream::HpackDebugVisitor { |
| void OnUseEntry(QuicTime::Delta elapsed) override { |
| UMA_HISTOGRAM_TIMES( |
| "Net.QuicHpackEncoder.IndexedEntryAge", |
| base::TimeDelta::FromMicroseconds(elapsed.ToMicroseconds())); |
| } |
| }; |
| |
| class HpackDecoderDebugVisitor : public QuicHeadersStream::HpackDebugVisitor { |
| void OnUseEntry(QuicTime::Delta elapsed) override { |
| UMA_HISTOGRAM_TIMES( |
| "Net.QuicHpackDecoder.IndexedEntryAge", |
| base::TimeDelta::FromMicroseconds(elapsed.ToMicroseconds())); |
| } |
| }; |
| |
| } // namespace |
| |
| QuicChromiumClientSession::StreamRequest::StreamRequest() : stream_(nullptr) {} |
| |
| QuicChromiumClientSession::StreamRequest::~StreamRequest() { |
| CancelRequest(); |
| } |
| |
| int QuicChromiumClientSession::StreamRequest::StartRequest( |
| const base::WeakPtr<QuicChromiumClientSession>& session, |
| QuicChromiumClientStream** stream, |
| const CompletionCallback& callback) { |
| session_ = session; |
| stream_ = stream; |
| int rv = session_->TryCreateStream(this, stream_); |
| if (rv == ERR_IO_PENDING) { |
| callback_ = callback; |
| } |
| |
| return rv; |
| } |
| |
| void QuicChromiumClientSession::StreamRequest::CancelRequest() { |
| if (session_) |
| session_->CancelRequest(this); |
| session_.reset(); |
| callback_.Reset(); |
| } |
| |
| void QuicChromiumClientSession::StreamRequest::OnRequestCompleteSuccess( |
| QuicChromiumClientStream* stream) { |
| session_.reset(); |
| *stream_ = stream; |
| base::ResetAndReturn(&callback_).Run(OK); |
| } |
| |
| void QuicChromiumClientSession::StreamRequest::OnRequestCompleteFailure( |
| int rv) { |
| session_.reset(); |
| base::ResetAndReturn(&callback_).Run(rv); |
| } |
| |
| QuicChromiumClientSession::QuicChromiumClientSession( |
| QuicConnection* connection, |
| std::unique_ptr<DatagramClientSocket> socket, |
| QuicStreamFactory* stream_factory, |
| QuicCryptoClientStreamFactory* crypto_client_stream_factory, |
| QuicClock* clock, |
| TransportSecurityState* transport_security_state, |
| std::unique_ptr<QuicServerInfo> server_info, |
| const QuicServerId& server_id, |
| int yield_after_packets, |
| QuicTime::Delta yield_after_duration, |
| int cert_verify_flags, |
| const QuicConfig& config, |
| QuicCryptoClientConfig* crypto_config, |
| const char* const connection_description, |
| base::TimeTicks dns_resolution_end_time, |
| QuicClientPushPromiseIndex* push_promise_index, |
| base::TaskRunner* task_runner, |
| std::unique_ptr<SocketPerformanceWatcher> socket_performance_watcher, |
| NetLog* net_log) |
| : QuicClientSessionBase(connection, push_promise_index, config), |
| server_id_(server_id), |
| require_confirmation_(false), |
| stream_factory_(stream_factory), |
| transport_security_state_(transport_security_state), |
| server_info_(std::move(server_info)), |
| pkp_bypassed_(false), |
| num_total_streams_(0), |
| task_runner_(task_runner), |
| net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_QUIC_SESSION)), |
| dns_resolution_end_time_(dns_resolution_end_time), |
| logger_(new QuicConnectionLogger(this, |
| connection_description, |
| std::move(socket_performance_watcher), |
| net_log_)), |
| going_away_(false), |
| port_migration_detected_(false), |
| disabled_reason_(QUIC_DISABLED_NOT), |
| token_binding_signatures_(kTokenBindingSignatureMapSize), |
| streams_pushed_count_(0), |
| streams_pushed_and_claimed_count_(0), |
| weak_factory_(this) { |
| sockets_.push_back(std::move(socket)); |
| packet_readers_.push_back(base::WrapUnique(new QuicChromiumPacketReader( |
| sockets_.back().get(), clock, this, yield_after_packets, |
| yield_after_duration, net_log_))); |
| crypto_stream_.reset( |
| crypto_client_stream_factory->CreateQuicCryptoClientStream( |
| server_id, this, base::WrapUnique(new ProofVerifyContextChromium( |
| cert_verify_flags, net_log_)), |
| crypto_config)); |
| connection->set_debug_visitor(logger_.get()); |
| connection->set_creator_debug_delegate(logger_.get()); |
| net_log_.BeginEvent(NetLog::TYPE_QUIC_SESSION, |
| base::Bind(NetLogQuicClientSessionCallback, &server_id, |
| cert_verify_flags, require_confirmation_)); |
| IPEndPoint address; |
| if (socket && socket->GetLocalAddress(&address) == OK && |
| address.GetFamily() == ADDRESS_FAMILY_IPV6) { |
| connection->SetMaxPacketLength(connection->max_packet_length() - |
| kAdditionalOverheadForIPv6); |
| } |
| } |
| |
| QuicChromiumClientSession::~QuicChromiumClientSession() { |
| if (!dynamic_streams().empty()) |
| RecordUnexpectedOpenStreams(DESTRUCTOR); |
| if (!observers_.empty()) |
| RecordUnexpectedObservers(DESTRUCTOR); |
| if (!going_away_) |
| RecordUnexpectedNotGoingAway(DESTRUCTOR); |
| |
| while (!dynamic_streams().empty() || !observers_.empty() || |
| !stream_requests_.empty()) { |
| // The session must be closed before it is destroyed. |
| DCHECK(dynamic_streams().empty()); |
| CloseAllStreams(ERR_UNEXPECTED); |
| DCHECK(observers_.empty()); |
| CloseAllObservers(ERR_UNEXPECTED); |
| |
| connection()->set_debug_visitor(nullptr); |
| net_log_.EndEvent(NetLog::TYPE_QUIC_SESSION); |
| |
| while (!stream_requests_.empty()) { |
| StreamRequest* request = stream_requests_.front(); |
| stream_requests_.pop_front(); |
| request->OnRequestCompleteFailure(ERR_ABORTED); |
| } |
| } |
| |
| if (connection()->connected()) { |
| // Ensure that the connection is closed by the time the session is |
| // destroyed. |
| connection()->CloseConnection(QUIC_INTERNAL_ERROR, "session torn down", |
| ConnectionCloseBehavior::SILENT_CLOSE); |
| } |
| |
| if (IsEncryptionEstablished()) |
| RecordHandshakeState(STATE_ENCRYPTION_ESTABLISHED); |
| if (IsCryptoHandshakeConfirmed()) |
| RecordHandshakeState(STATE_HANDSHAKE_CONFIRMED); |
| else |
| RecordHandshakeState(STATE_FAILED); |
| |
| UMA_HISTOGRAM_COUNTS("Net.QuicSession.NumTotalStreams", num_total_streams_); |
| UMA_HISTOGRAM_COUNTS("Net.QuicNumSentClientHellos", |
| crypto_stream_->num_sent_client_hellos()); |
| UMA_HISTOGRAM_COUNTS("Net.QuicSession.Pushed", streams_pushed_count_); |
| UMA_HISTOGRAM_COUNTS("Net.QuicSession.PushedAndClaimed", |
| streams_pushed_and_claimed_count_); |
| if (!IsCryptoHandshakeConfirmed()) |
| return; |
| |
| // Sending one client_hello means we had zero handshake-round-trips. |
| int round_trip_handshakes = crypto_stream_->num_sent_client_hellos() - 1; |
| |
| // Don't bother with these histogram during tests, which mock out |
| // num_sent_client_hellos(). |
| if (round_trip_handshakes < 0 || !stream_factory_) |
| return; |
| |
| bool port_selected = stream_factory_->enable_port_selection(); |
| SSLInfo ssl_info; |
| // QUIC supports only secure urls. |
| if (GetSSLInfo(&ssl_info) && ssl_info.cert.get()) { |
| if (!port_selected) { |
| UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectRandomPortForHTTPS", |
| round_trip_handshakes, 0, 3, 4); |
| if (require_confirmation_) { |
| UMA_HISTOGRAM_CUSTOM_COUNTS( |
| "Net.QuicSession.ConnectRandomPortRequiringConfirmationForHTTPS", |
| round_trip_handshakes, 0, 3, 4); |
| } |
| } |
| } |
| const QuicConnectionStats stats = connection()->GetStats(); |
| if (server_info_ && stats.min_rtt_us > 0) { |
| base::TimeTicks wait_for_data_start_time = |
| server_info_->wait_for_data_start_time(); |
| base::TimeTicks wait_for_data_end_time = |
| server_info_->wait_for_data_end_time(); |
| if (!wait_for_data_start_time.is_null() && |
| !wait_for_data_end_time.is_null()) { |
| base::TimeDelta wait_time = |
| wait_for_data_end_time - wait_for_data_start_time; |
| const base::HistogramBase::Sample kMaxWaitToRtt = 1000; |
| base::HistogramBase::Sample wait_to_rtt = |
| static_cast<base::HistogramBase::Sample>( |
| 100 * wait_time.InMicroseconds() / stats.min_rtt_us); |
| UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicServerInfo.WaitForDataReadyToRtt", |
| wait_to_rtt, 0, kMaxWaitToRtt, 50); |
| } |
| } |
| |
| // The MTU used by QUIC is limited to a fairly small set of predefined values |
| // (initial values and MTU discovery values), but does not fare well when |
| // bucketed. Because of that, a sparse histogram is used here. |
| UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.ClientSideMtu", |
| connection()->max_packet_length()); |
| UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.ServerSideMtu", |
| stats.max_received_packet_size); |
| |
| UMA_HISTOGRAM_COUNTS("Net.QuicSession.MtuProbesSent", |
| connection()->mtu_probe_count()); |
| |
| if (stats.max_sequence_reordering == 0) |
| return; |
| const base::HistogramBase::Sample kMaxReordering = 100; |
| base::HistogramBase::Sample reordering = kMaxReordering; |
| if (stats.min_rtt_us > 0) { |
| reordering = static_cast<base::HistogramBase::Sample>( |
| 100 * stats.max_time_reordering_us / stats.min_rtt_us); |
| } |
| UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.MaxReorderingTime", reordering, |
| 0, kMaxReordering, 50); |
| if (stats.min_rtt_us > 100 * 1000) { |
| UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.MaxReorderingTimeLongRtt", |
| reordering, 0, kMaxReordering, 50); |
| } |
| UMA_HISTOGRAM_COUNTS( |
| "Net.QuicSession.MaxReordering", |
| static_cast<base::HistogramBase::Sample>(stats.max_sequence_reordering)); |
| } |
| |
| void QuicChromiumClientSession::Initialize() { |
| QuicClientSessionBase::Initialize(); |
| headers_stream()->SetHpackEncoderDebugVisitor( |
| base::WrapUnique(new HpackEncoderDebugVisitor())); |
| headers_stream()->SetHpackDecoderDebugVisitor( |
| base::WrapUnique(new HpackDecoderDebugVisitor())); |
| } |
| |
| void QuicChromiumClientSession::OnHeadersHeadOfLineBlocking( |
| QuicTime::Delta delta) { |
| UMA_HISTOGRAM_TIMES( |
| "Net.QuicSession.HeadersHOLBlockedTime", |
| base::TimeDelta::FromMicroseconds(delta.ToMicroseconds())); |
| } |
| |
| void QuicChromiumClientSession::OnStreamFrame(const QuicStreamFrame& frame) { |
| // Record total number of stream frames. |
| UMA_HISTOGRAM_COUNTS("Net.QuicNumStreamFramesInPacket", 1); |
| |
| // Record number of frames per stream in packet. |
| UMA_HISTOGRAM_COUNTS("Net.QuicNumStreamFramesPerStreamInPacket", 1); |
| |
| return QuicSpdySession::OnStreamFrame(frame); |
| } |
| |
| void QuicChromiumClientSession::AddObserver(Observer* observer) { |
| if (going_away_) { |
| RecordUnexpectedObservers(ADD_OBSERVER); |
| observer->OnSessionClosed(ERR_UNEXPECTED, port_migration_detected_); |
| return; |
| } |
| |
| DCHECK(!ContainsKey(observers_, observer)); |
| observers_.insert(observer); |
| } |
| |
| void QuicChromiumClientSession::RemoveObserver(Observer* observer) { |
| DCHECK(ContainsKey(observers_, observer)); |
| observers_.erase(observer); |
| } |
| |
| int QuicChromiumClientSession::TryCreateStream( |
| StreamRequest* request, |
| QuicChromiumClientStream** stream) { |
| if (!crypto_stream_->encryption_established()) { |
| DLOG(DFATAL) << "Encryption not established."; |
| return ERR_CONNECTION_CLOSED; |
| } |
| |
| if (goaway_received()) { |
| DVLOG(1) << "Going away."; |
| return ERR_CONNECTION_CLOSED; |
| } |
| |
| if (!connection()->connected()) { |
| DVLOG(1) << "Already closed."; |
| return ERR_CONNECTION_CLOSED; |
| } |
| |
| if (going_away_) { |
| RecordUnexpectedOpenStreams(TRY_CREATE_STREAM); |
| return ERR_CONNECTION_CLOSED; |
| } |
| |
| if (GetNumOpenOutgoingStreams() < max_open_outgoing_streams()) { |
| *stream = CreateOutgoingReliableStreamImpl(); |
| return OK; |
| } |
| |
| stream_requests_.push_back(request); |
| return ERR_IO_PENDING; |
| } |
| |
| void QuicChromiumClientSession::CancelRequest(StreamRequest* request) { |
| // Remove |request| from the queue while preserving the order of the |
| // other elements. |
| StreamRequestQueue::iterator it = |
| std::find(stream_requests_.begin(), stream_requests_.end(), request); |
| if (it != stream_requests_.end()) { |
| it = stream_requests_.erase(it); |
| } |
| } |
| |
| bool QuicChromiumClientSession::ShouldCreateOutgoingDynamicStream() { |
| if (!crypto_stream_->encryption_established()) { |
| DVLOG(1) << "Encryption not active so no outgoing stream created."; |
| return false; |
| } |
| if (GetNumOpenOutgoingStreams() >= max_open_outgoing_streams()) { |
| DVLOG(1) << "Failed to create a new outgoing stream. " |
| << "Already " << GetNumOpenOutgoingStreams() << " open."; |
| return false; |
| } |
| if (goaway_received()) { |
| DVLOG(1) << "Failed to create a new outgoing stream. " |
| << "Already received goaway."; |
| return false; |
| } |
| if (going_away_) { |
| RecordUnexpectedOpenStreams(CREATE_OUTGOING_RELIABLE_STREAM); |
| return false; |
| } |
| return true; |
| } |
| |
| QuicChromiumClientStream* |
| QuicChromiumClientSession::CreateOutgoingDynamicStream(SpdyPriority priority) { |
| if (!ShouldCreateOutgoingDynamicStream()) { |
| return nullptr; |
| } |
| return CreateOutgoingReliableStreamImpl(); |
| } |
| |
| QuicChromiumClientStream* |
| QuicChromiumClientSession::CreateOutgoingReliableStreamImpl() { |
| DCHECK(connection()->connected()); |
| QuicChromiumClientStream* stream = |
| new QuicChromiumClientStream(GetNextOutgoingStreamId(), this, net_log_); |
| ActivateStream(stream); |
| ++num_total_streams_; |
| UMA_HISTOGRAM_COUNTS("Net.QuicSession.NumOpenStreams", |
| GetNumOpenOutgoingStreams()); |
| // The previous histogram puts 100 in a bucket betweeen 86-113 which does |
| // not shed light on if chrome ever things it has more than 100 streams open. |
| UMA_HISTOGRAM_BOOLEAN("Net.QuicSession.TooManyOpenStreams", |
| GetNumOpenOutgoingStreams() > 100); |
| return stream; |
| } |
| |
| QuicCryptoClientStream* QuicChromiumClientSession::GetCryptoStream() { |
| return crypto_stream_.get(); |
| } |
| |
| // TODO(rtenneti): Add unittests for GetSSLInfo which exercise the various ways |
| // we learn about SSL info (sync vs async vs cached). |
| bool QuicChromiumClientSession::GetSSLInfo(SSLInfo* ssl_info) const { |
| ssl_info->Reset(); |
| if (!cert_verify_result_) { |
| return false; |
| } |
| |
| ssl_info->cert_status = cert_verify_result_->cert_status; |
| ssl_info->cert = cert_verify_result_->verified_cert; |
| |
| // TODO(wtc): Define QUIC "cipher suites". |
| // Report the TLS cipher suite that most closely resembles the crypto |
| // parameters of the QUIC connection. |
| QuicTag aead = crypto_stream_->crypto_negotiated_params().aead; |
| uint16_t cipher_suite; |
| int security_bits; |
| switch (aead) { |
| case kAESG: |
| cipher_suite = 0xc02f; // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 |
| security_bits = 128; |
| break; |
| case kCC20: |
| cipher_suite = 0xcc13; // TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 |
| security_bits = 256; |
| break; |
| default: |
| NOTREACHED(); |
| return false; |
| } |
| int ssl_connection_status = 0; |
| ssl_connection_status |= cipher_suite; |
| ssl_connection_status |= |
| (SSL_CONNECTION_VERSION_QUIC & SSL_CONNECTION_VERSION_MASK) |
| << SSL_CONNECTION_VERSION_SHIFT; |
| |
| ssl_info->public_key_hashes = cert_verify_result_->public_key_hashes; |
| ssl_info->is_issued_by_known_root = |
| cert_verify_result_->is_issued_by_known_root; |
| ssl_info->pkp_bypassed = pkp_bypassed_; |
| |
| ssl_info->connection_status = ssl_connection_status; |
| ssl_info->client_cert_sent = false; |
| ssl_info->channel_id_sent = crypto_stream_->WasChannelIDSent(); |
| ssl_info->security_bits = security_bits; |
| ssl_info->handshake_type = SSLInfo::HANDSHAKE_FULL; |
| ssl_info->pinning_failure_log = pinning_failure_log_; |
| |
| ssl_info->UpdateCertificateTransparencyInfo(*ct_verify_result_); |
| |
| if (crypto_stream_->crypto_negotiated_params().token_binding_key_param == |
| kP256) { |
| ssl_info->token_binding_negotiated = true; |
| ssl_info->token_binding_key_param = TB_PARAM_ECDSAP256; |
| } |
| |
| return true; |
| } |
| |
| Error QuicChromiumClientSession::GetTokenBindingSignature( |
| crypto::ECPrivateKey* key, |
| std::vector<uint8_t>* out) { |
| // The same key will be used across multiple requests to sign the same value, |
| // so the signature is cached. |
| std::string raw_public_key; |
| if (!key->ExportRawPublicKey(&raw_public_key)) |
| return ERR_FAILED; |
| TokenBindingSignatureMap::iterator it = |
| token_binding_signatures_.Get(raw_public_key); |
| if (it != token_binding_signatures_.end()) { |
| *out = it->second; |
| return OK; |
| } |
| |
| std::string key_material; |
| if (!crypto_stream_->ExportTokenBindingKeyingMaterial(&key_material)) |
| return ERR_FAILED; |
| if (!SignTokenBindingEkm(key_material, key, out)) |
| return ERR_FAILED; |
| token_binding_signatures_.Put(raw_public_key, *out); |
| return OK; |
| } |
| |
| int QuicChromiumClientSession::CryptoConnect( |
| bool require_confirmation, |
| const CompletionCallback& callback) { |
| require_confirmation_ = require_confirmation; |
| handshake_start_ = base::TimeTicks::Now(); |
| RecordHandshakeState(STATE_STARTED); |
| DCHECK(flow_controller()); |
| crypto_stream_->CryptoConnect(); |
| |
| if (IsCryptoHandshakeConfirmed()) |
| return OK; |
| |
| // Unless we require handshake confirmation, activate the session if |
| // we have established initial encryption. |
| if (!require_confirmation_ && IsEncryptionEstablished()) { |
| // To mitigate the effects of hanging 0-RTT connections, set up a timer to |
| // cancel any requests, if the handshake takes too long. |
| task_runner_->PostDelayedTask( |
| FROM_HERE, base::Bind(&QuicChromiumClientSession::OnConnectTimeout, |
| weak_factory_.GetWeakPtr()), |
| base::TimeDelta::FromMilliseconds(k0RttHandshakeTimeoutMs)); |
| return OK; |
| } |
| |
| callback_ = callback; |
| return ERR_IO_PENDING; |
| } |
| |
| int QuicChromiumClientSession::ResumeCryptoConnect( |
| const CompletionCallback& callback) { |
| if (IsCryptoHandshakeConfirmed()) |
| return OK; |
| |
| if (!connection()->connected()) |
| return ERR_QUIC_HANDSHAKE_FAILED; |
| |
| callback_ = callback; |
| return ERR_IO_PENDING; |
| } |
| |
| int QuicChromiumClientSession::GetNumSentClientHellos() const { |
| return crypto_stream_->num_sent_client_hellos(); |
| } |
| |
| bool QuicChromiumClientSession::CanPool(const std::string& hostname, |
| PrivacyMode privacy_mode) const { |
| DCHECK(connection()->connected()); |
| if (privacy_mode != server_id_.privacy_mode()) { |
| // Privacy mode must always match. |
| return false; |
| } |
| SSLInfo ssl_info; |
| if (!GetSSLInfo(&ssl_info) || !ssl_info.cert.get()) { |
| NOTREACHED() << "QUIC should always have certificates."; |
| return false; |
| } |
| |
| return SpdySession::CanPool(transport_security_state_, ssl_info, |
| server_id_.host(), hostname); |
| } |
| |
| bool QuicChromiumClientSession::ShouldCreateIncomingDynamicStream( |
| QuicStreamId id) { |
| if (!connection()->connected()) { |
| LOG(DFATAL) << "ShouldCreateIncomingDynamicStream called when disconnected"; |
| return false; |
| } |
| if (goaway_received()) { |
| DVLOG(1) << "Cannot create a new outgoing stream. " |
| << "Already received goaway."; |
| return false; |
| } |
| if (going_away_) { |
| return false; |
| } |
| if (id % 2 != 0) { |
| LOG(WARNING) << "Received invalid push stream id " << id; |
| connection()->CloseConnection( |
| QUIC_INVALID_STREAM_ID, "Server created odd numbered stream", |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return false; |
| } |
| return true; |
| } |
| |
| QuicChromiumClientStream* |
| QuicChromiumClientSession::CreateIncomingDynamicStream(QuicStreamId id) { |
| if (!ShouldCreateIncomingDynamicStream(id)) { |
| return nullptr; |
| } |
| return CreateIncomingReliableStreamImpl(id); |
| } |
| |
| QuicChromiumClientStream* |
| QuicChromiumClientSession::CreateIncomingReliableStreamImpl(QuicStreamId id) { |
| DCHECK(connection()->connected()); |
| QuicChromiumClientStream* stream = |
| new QuicChromiumClientStream(id, this, net_log_); |
| stream->CloseWriteSide(); |
| ActivateStream(stream); |
| ++num_total_streams_; |
| return stream; |
| } |
| |
| void QuicChromiumClientSession::CloseStream(QuicStreamId stream_id) { |
| ReliableQuicStream* stream = GetOrCreateStream(stream_id); |
| if (stream) { |
| logger_->UpdateReceivedFrameCounts(stream_id, stream->num_frames_received(), |
| stream->num_duplicate_frames_received()); |
| } |
| QuicSpdySession::CloseStream(stream_id); |
| OnClosedStream(); |
| } |
| |
| void QuicChromiumClientSession::SendRstStream(QuicStreamId id, |
| QuicRstStreamErrorCode error, |
| QuicStreamOffset bytes_written) { |
| QuicSpdySession::SendRstStream(id, error, bytes_written); |
| OnClosedStream(); |
| } |
| |
| void QuicChromiumClientSession::OnClosedStream() { |
| if (GetNumOpenOutgoingStreams() < max_open_outgoing_streams() && |
| !stream_requests_.empty() && crypto_stream_->encryption_established() && |
| !goaway_received() && !going_away_ && connection()->connected()) { |
| StreamRequest* request = stream_requests_.front(); |
| stream_requests_.pop_front(); |
| request->OnRequestCompleteSuccess(CreateOutgoingReliableStreamImpl()); |
| } |
| |
| if (GetNumOpenOutgoingStreams() == 0 && stream_factory_) { |
| stream_factory_->OnIdleSession(this); |
| } |
| } |
| |
| void QuicChromiumClientSession::OnCryptoHandshakeEvent( |
| CryptoHandshakeEvent event) { |
| if (stream_factory_ && event == HANDSHAKE_CONFIRMED && |
| (stream_factory_->OnHandshakeConfirmed( |
| this, logger_->ReceivedPacketLossRate()))) { |
| return; |
| } |
| |
| if (!callback_.is_null() && |
| (!require_confirmation_ || event == HANDSHAKE_CONFIRMED || |
| event == ENCRYPTION_REESTABLISHED)) { |
| // TODO(rtenneti): Currently for all CryptoHandshakeEvent events, callback_ |
| // could be called because there are no error events in CryptoHandshakeEvent |
| // enum. If error events are added to CryptoHandshakeEvent, then the |
| // following code needs to changed. |
| base::ResetAndReturn(&callback_).Run(OK); |
| } |
| if (event == HANDSHAKE_CONFIRMED) { |
| UMA_HISTOGRAM_TIMES("Net.QuicSession.HandshakeConfirmedTime", |
| base::TimeTicks::Now() - handshake_start_); |
| if (server_info_) { |
| // TODO(rtenneti): Should we delete this histogram? |
| // Track how long it has taken to finish handshake once we start waiting |
| // for reading of QUIC server information from disk cache. We could use |
| // this data to compare total time taken if we were to cancel the disk |
| // cache read vs waiting for the read to complete. |
| base::TimeTicks wait_for_data_start_time = |
| server_info_->wait_for_data_start_time(); |
| if (!wait_for_data_start_time.is_null()) { |
| UMA_HISTOGRAM_TIMES( |
| "Net.QuicServerInfo.WaitForDataReady.HandshakeConfirmedTime", |
| base::TimeTicks::Now() - wait_for_data_start_time); |
| } |
| } |
| // Track how long it has taken to finish handshake after we have finished |
| // DNS host resolution. |
| if (!dns_resolution_end_time_.is_null()) { |
| UMA_HISTOGRAM_TIMES( |
| "Net.QuicSession.HostResolution.HandshakeConfirmedTime", |
| base::TimeTicks::Now() - dns_resolution_end_time_); |
| } |
| |
| ObserverSet::iterator it = observers_.begin(); |
| while (it != observers_.end()) { |
| Observer* observer = *it; |
| ++it; |
| observer->OnCryptoHandshakeConfirmed(); |
| } |
| if (server_info_) |
| server_info_->OnExternalCacheHit(); |
| } |
| QuicSpdySession::OnCryptoHandshakeEvent(event); |
| } |
| |
| void QuicChromiumClientSession::OnCryptoHandshakeMessageSent( |
| const CryptoHandshakeMessage& message) { |
| logger_->OnCryptoHandshakeMessageSent(message); |
| |
| if (message.tag() == kREJ || message.tag() == kSREJ) { |
| UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.RejectLength", |
| message.GetSerialized().length(), 1000, 10000, |
| 50); |
| } |
| } |
| |
| void QuicChromiumClientSession::OnCryptoHandshakeMessageReceived( |
| const CryptoHandshakeMessage& message) { |
| logger_->OnCryptoHandshakeMessageReceived(message); |
| } |
| |
| void QuicChromiumClientSession::OnGoAway(const QuicGoAwayFrame& frame) { |
| QuicSession::OnGoAway(frame); |
| NotifyFactoryOfSessionGoingAway(); |
| port_migration_detected_ = frame.error_code == QUIC_ERROR_MIGRATING_PORT; |
| } |
| |
| void QuicChromiumClientSession::OnRstStream(const QuicRstStreamFrame& frame) { |
| QuicSession::OnRstStream(frame); |
| OnClosedStream(); |
| } |
| |
| void QuicChromiumClientSession::OnConnectionClosed( |
| QuicErrorCode error, |
| const std::string& error_details, |
| ConnectionCloseSource source) { |
| DCHECK(!connection()->connected()); |
| logger_->OnConnectionClosed(error, error_details, source); |
| if (source == ConnectionCloseSource::FROM_PEER) { |
| if (IsCryptoHandshakeConfirmed()) { |
| UMA_HISTOGRAM_SPARSE_SLOWLY( |
| "Net.QuicSession.ConnectionCloseErrorCodeServer.HandshakeConfirmed", |
| error); |
| base::HistogramBase* histogram = base::SparseHistogram::FactoryGet( |
| "Net.QuicSession.StreamCloseErrorCodeServer.HandshakeConfirmed", |
| base::HistogramBase::kUmaTargetedHistogramFlag); |
| size_t num_streams = GetNumActiveStreams(); |
| if (num_streams > 0) |
| histogram->AddCount(error, num_streams); |
| } |
| UMA_HISTOGRAM_SPARSE_SLOWLY( |
| "Net.QuicSession.ConnectionCloseErrorCodeServer", error); |
| } else { |
| if (IsCryptoHandshakeConfirmed()) { |
| UMA_HISTOGRAM_SPARSE_SLOWLY( |
| "Net.QuicSession.ConnectionCloseErrorCodeClient.HandshakeConfirmed", |
| error); |
| base::HistogramBase* histogram = base::SparseHistogram::FactoryGet( |
| "Net.QuicSession.StreamCloseErrorCodeClient.HandshakeConfirmed", |
| base::HistogramBase::kUmaTargetedHistogramFlag); |
| size_t num_streams = GetNumActiveStreams(); |
| if (num_streams > 0) |
| histogram->AddCount(error, num_streams); |
| } |
| UMA_HISTOGRAM_SPARSE_SLOWLY( |
| "Net.QuicSession.ConnectionCloseErrorCodeClient", error); |
| } |
| |
| if (error == QUIC_NETWORK_IDLE_TIMEOUT) { |
| UMA_HISTOGRAM_COUNTS( |
| "Net.QuicSession.ConnectionClose.NumOpenStreams.TimedOut", |
| GetNumOpenOutgoingStreams()); |
| if (IsCryptoHandshakeConfirmed()) { |
| if (GetNumOpenOutgoingStreams() > 0) { |
| disabled_reason_ = QUIC_DISABLED_TIMEOUT_WITH_OPEN_STREAMS; |
| UMA_HISTOGRAM_BOOLEAN( |
| "Net.QuicSession.TimedOutWithOpenStreams.HasUnackedPackets", |
| connection()->sent_packet_manager().HasUnackedPackets()); |
| UMA_HISTOGRAM_COUNTS( |
| "Net.QuicSession.TimedOutWithOpenStreams.ConsecutiveRTOCount", |
| connection()->sent_packet_manager().GetConsecutiveRtoCount()); |
| UMA_HISTOGRAM_COUNTS( |
| "Net.QuicSession.TimedOutWithOpenStreams.ConsecutiveTLPCount", |
| connection()->sent_packet_manager().GetConsecutiveTlpCount()); |
| } |
| if (connection()->sent_packet_manager().HasUnackedPackets()) { |
| UMA_HISTOGRAM_TIMES( |
| "Net.QuicSession.LocallyTimedOutWithOpenStreams." |
| "TimeSinceLastReceived.UnackedPackets", |
| NetworkActivityMonitor::GetInstance()->GetTimeSinceLastReceived()); |
| } else { |
| UMA_HISTOGRAM_TIMES( |
| "Net.QuicSession.LocallyTimedOutWithOpenStreams." |
| "TimeSinceLastReceived.NoUnackedPackets", |
| NetworkActivityMonitor::GetInstance()->GetTimeSinceLastReceived()); |
| } |
| |
| } else { |
| UMA_HISTOGRAM_COUNTS( |
| "Net.QuicSession.ConnectionClose.NumOpenStreams.HandshakeTimedOut", |
| GetNumOpenOutgoingStreams()); |
| UMA_HISTOGRAM_COUNTS( |
| "Net.QuicSession.ConnectionClose.NumTotalStreams.HandshakeTimedOut", |
| num_total_streams_); |
| } |
| } |
| |
| if (!IsCryptoHandshakeConfirmed()) { |
| if (error == QUIC_PUBLIC_RESET) { |
| RecordHandshakeFailureReason(HANDSHAKE_FAILURE_PUBLIC_RESET); |
| } else if (connection()->GetStats().packets_received == 0) { |
| RecordHandshakeFailureReason(HANDSHAKE_FAILURE_BLACK_HOLE); |
| UMA_HISTOGRAM_SPARSE_SLOWLY( |
| "Net.QuicSession.ConnectionClose.HandshakeFailureBlackHole.QuicError", |
| error); |
| } else { |
| RecordHandshakeFailureReason(HANDSHAKE_FAILURE_UNKNOWN); |
| UMA_HISTOGRAM_SPARSE_SLOWLY( |
| "Net.QuicSession.ConnectionClose.HandshakeFailureUnknown.QuicError", |
| error); |
| } |
| } else if (error == QUIC_PUBLIC_RESET) { |
| disabled_reason_ = QUIC_DISABLED_PUBLIC_RESET_POST_HANDSHAKE; |
| } |
| |
| UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.QuicVersion", |
| connection()->version()); |
| NotifyFactoryOfSessionGoingAway(); |
| QuicSession::OnConnectionClosed(error, error_details, source); |
| |
| if (!callback_.is_null()) { |
| base::ResetAndReturn(&callback_).Run(ERR_QUIC_PROTOCOL_ERROR); |
| } |
| |
| for (auto& socket : sockets_) { |
| socket->Close(); |
| } |
| DCHECK(dynamic_streams().empty()); |
| CloseAllStreams(ERR_UNEXPECTED); |
| CloseAllObservers(ERR_UNEXPECTED); |
| NotifyFactoryOfSessionClosedLater(); |
| } |
| |
| void QuicChromiumClientSession::OnSuccessfulVersionNegotiation( |
| const QuicVersion& version) { |
| logger_->OnSuccessfulVersionNegotiation(version); |
| QuicSpdySession::OnSuccessfulVersionNegotiation(version); |
| } |
| |
| void QuicChromiumClientSession::OnPathDegrading() { |
| if (stream_factory_) { |
| stream_factory_->MaybeMigrateSessionEarly(this); |
| } |
| } |
| |
| bool QuicChromiumClientSession::HasOpenDynamicStreams() const { |
| return QuicSession::HasOpenDynamicStreams() || |
| GetNumDrainingOutgoingStreams() > 0; |
| } |
| |
| void QuicChromiumClientSession::OnProofValid( |
| const QuicCryptoClientConfig::CachedState& cached) { |
| DCHECK(cached.proof_valid()); |
| |
| if (!server_info_) { |
| return; |
| } |
| |
| QuicServerInfo::State* state = server_info_->mutable_state(); |
| |
| state->server_config = cached.server_config(); |
| state->source_address_token = cached.source_address_token(); |
| state->cert_sct = cached.cert_sct(); |
| state->chlo_hash = cached.chlo_hash(); |
| state->server_config_sig = cached.signature(); |
| state->certs = cached.certs(); |
| |
| server_info_->Persist(); |
| } |
| |
| void QuicChromiumClientSession::OnProofVerifyDetailsAvailable( |
| const ProofVerifyDetails& verify_details) { |
| const ProofVerifyDetailsChromium* verify_details_chromium = |
| reinterpret_cast<const ProofVerifyDetailsChromium*>(&verify_details); |
| cert_verify_result_.reset( |
| new CertVerifyResult(verify_details_chromium->cert_verify_result)); |
| pinning_failure_log_ = verify_details_chromium->pinning_failure_log; |
| std::unique_ptr<ct::CTVerifyResult> ct_verify_result_copy( |
| new ct::CTVerifyResult(verify_details_chromium->ct_verify_result)); |
| ct_verify_result_ = std::move(ct_verify_result_copy); |
| logger_->OnCertificateVerified(*cert_verify_result_); |
| pkp_bypassed_ = verify_details_chromium->pkp_bypassed; |
| } |
| |
| void QuicChromiumClientSession::StartReading() { |
| for (auto& packet_reader : packet_readers_) { |
| packet_reader->StartReading(); |
| } |
| } |
| |
| void QuicChromiumClientSession::CloseSessionOnError(int error, |
| QuicErrorCode quic_error) { |
| RecordAndCloseSessionOnError(error, quic_error); |
| NotifyFactoryOfSessionClosed(); |
| } |
| |
| void QuicChromiumClientSession::CloseSessionOnErrorAndNotifyFactoryLater( |
| int error, |
| QuicErrorCode quic_error) { |
| RecordAndCloseSessionOnError(error, quic_error); |
| NotifyFactoryOfSessionClosedLater(); |
| } |
| |
| void QuicChromiumClientSession::RecordAndCloseSessionOnError( |
| int error, |
| QuicErrorCode quic_error) { |
| UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.CloseSessionOnError", -error); |
| CloseSessionOnErrorInner(error, quic_error); |
| } |
| |
| void QuicChromiumClientSession::CloseSessionOnErrorInner( |
| int net_error, |
| QuicErrorCode quic_error) { |
| if (!callback_.is_null()) { |
| base::ResetAndReturn(&callback_).Run(net_error); |
| } |
| CloseAllStreams(net_error); |
| CloseAllObservers(net_error); |
| net_log_.AddEvent(NetLog::TYPE_QUIC_SESSION_CLOSE_ON_ERROR, |
| NetLog::IntCallback("net_error", net_error)); |
| |
| if (connection()->connected()) |
| connection()->CloseConnection(quic_error, "net error", |
| ConnectionCloseBehavior::SILENT_CLOSE); |
| DCHECK(!connection()->connected()); |
| } |
| |
| void QuicChromiumClientSession::CloseAllStreams(int net_error) { |
| while (!dynamic_streams().empty()) { |
| ReliableQuicStream* stream = dynamic_streams().begin()->second; |
| QuicStreamId id = stream->id(); |
| static_cast<QuicChromiumClientStream*>(stream)->OnError(net_error); |
| CloseStream(id); |
| } |
| } |
| |
| void QuicChromiumClientSession::CloseAllObservers(int net_error) { |
| while (!observers_.empty()) { |
| Observer* observer = *observers_.begin(); |
| observers_.erase(observer); |
| observer->OnSessionClosed(net_error, port_migration_detected_); |
| } |
| } |
| |
| std::unique_ptr<base::Value> QuicChromiumClientSession::GetInfoAsValue( |
| const std::set<HostPortPair>& aliases) { |
| std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); |
| dict->SetString("version", QuicVersionToString(connection()->version())); |
| dict->SetInteger("open_streams", GetNumOpenOutgoingStreams()); |
| std::unique_ptr<base::ListValue> stream_list(new base::ListValue()); |
| for (StreamMap::const_iterator it = dynamic_streams().begin(); |
| it != dynamic_streams().end(); ++it) { |
| stream_list->AppendString(base::UintToString(it->second->id())); |
| } |
| dict->Set("active_streams", std::move(stream_list)); |
| |
| dict->SetInteger("total_streams", num_total_streams_); |
| dict->SetString("peer_address", peer_address().ToString()); |
| dict->SetString("connection_id", base::Uint64ToString(connection_id())); |
| dict->SetBoolean("connected", connection()->connected()); |
| const QuicConnectionStats& stats = connection()->GetStats(); |
| dict->SetInteger("packets_sent", stats.packets_sent); |
| dict->SetInteger("packets_received", stats.packets_received); |
| dict->SetInteger("packets_lost", stats.packets_lost); |
| SSLInfo ssl_info; |
| dict->SetBoolean("secure", GetSSLInfo(&ssl_info) && ssl_info.cert.get()); |
| |
| std::unique_ptr<base::ListValue> alias_list(new base::ListValue()); |
| for (std::set<HostPortPair>::const_iterator it = aliases.begin(); |
| it != aliases.end(); it++) { |
| alias_list->AppendString(it->ToString()); |
| } |
| dict->Set("aliases", std::move(alias_list)); |
| |
| return std::move(dict); |
| } |
| |
| base::WeakPtr<QuicChromiumClientSession> |
| QuicChromiumClientSession::GetWeakPtr() { |
| return weak_factory_.GetWeakPtr(); |
| } |
| |
| void QuicChromiumClientSession::OnReadError( |
| int result, |
| const DatagramClientSocket* socket) { |
| DCHECK(socket != nullptr); |
| if (socket != GetDefaultSocket()) { |
| // Ignore read errors from old sockets that are no longer active. |
| // TODO(jri): Maybe clean up old sockets on error. |
| return; |
| } |
| DVLOG(1) << "Closing session on read error: " << result; |
| UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.ReadError", -result); |
| NotifyFactoryOfSessionGoingAway(); |
| CloseSessionOnErrorInner(result, QUIC_PACKET_READ_ERROR); |
| NotifyFactoryOfSessionClosedLater(); |
| } |
| |
| bool QuicChromiumClientSession::OnPacket(const QuicReceivedPacket& packet, |
| IPEndPoint local_address, |
| IPEndPoint peer_address) { |
| ProcessUdpPacket(local_address, peer_address, packet); |
| if (!connection()->connected()) { |
| NotifyFactoryOfSessionClosedLater(); |
| return false; |
| } |
| return true; |
| } |
| |
| void QuicChromiumClientSession::NotifyFactoryOfSessionGoingAway() { |
| going_away_ = true; |
| if (stream_factory_) |
| stream_factory_->OnSessionGoingAway(this); |
| } |
| |
| void QuicChromiumClientSession::NotifyFactoryOfSessionClosedLater() { |
| if (!dynamic_streams().empty()) |
| RecordUnexpectedOpenStreams(NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER); |
| |
| if (!going_away_) |
| RecordUnexpectedNotGoingAway(NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER); |
| |
| going_away_ = true; |
| DCHECK_EQ(0u, GetNumActiveStreams()); |
| DCHECK(!connection()->connected()); |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::Bind(&QuicChromiumClientSession::NotifyFactoryOfSessionClosed, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void QuicChromiumClientSession::NotifyFactoryOfSessionClosed() { |
| if (!dynamic_streams().empty()) |
| RecordUnexpectedOpenStreams(NOTIFY_FACTORY_OF_SESSION_CLOSED); |
| |
| if (!going_away_) |
| RecordUnexpectedNotGoingAway(NOTIFY_FACTORY_OF_SESSION_CLOSED); |
| |
| going_away_ = true; |
| DCHECK_EQ(0u, GetNumActiveStreams()); |
| // Will delete |this|. |
| if (stream_factory_) |
| stream_factory_->OnSessionClosed(this); |
| } |
| |
| void QuicChromiumClientSession::OnConnectTimeout() { |
| DCHECK(callback_.is_null()); |
| DCHECK(IsEncryptionEstablished()); |
| |
| if (IsCryptoHandshakeConfirmed()) |
| return; |
| |
| // TODO(rch): re-enable this code once beta is cut. |
| // if (stream_factory_) |
| // stream_factory_->OnSessionConnectTimeout(this); |
| // CloseAllStreams(ERR_QUIC_HANDSHAKE_FAILED); |
| // DCHECK_EQ(0u, GetNumOpenOutgoingStreams()); |
| } |
| |
| bool QuicChromiumClientSession::MigrateToSocket( |
| std::unique_ptr<DatagramClientSocket> socket, |
| std::unique_ptr<QuicChromiumPacketReader> reader, |
| std::unique_ptr<QuicPacketWriter> writer) { |
| DCHECK_EQ(sockets_.size(), packet_readers_.size()); |
| if (sockets_.size() >= kMaxReadersPerQuicSession) { |
| return false; |
| } |
| // TODO(jri): Make SetQuicPacketWriter take a scoped_ptr. |
| connection()->SetQuicPacketWriter(writer.release(), /*owns_writer=*/true); |
| packet_readers_.push_back(std::move(reader)); |
| sockets_.push_back(std::move(socket)); |
| StartReading(); |
| connection()->SendPing(); |
| return true; |
| } |
| |
| void QuicChromiumClientSession::PopulateNetErrorDetails( |
| NetErrorDetails* details) { |
| details->quic_port_migration_detected = port_migration_detected_; |
| } |
| |
| const DatagramClientSocket* QuicChromiumClientSession::GetDefaultSocket() |
| const { |
| DCHECK(sockets_.back().get() != nullptr); |
| // The most recently added socket is the currently active one. |
| return sockets_.back().get(); |
| } |
| |
| bool QuicChromiumClientSession::IsAuthorized(const std::string& hostname) { |
| bool result = CanPool(hostname, server_id_.privacy_mode()); |
| if (result) |
| streams_pushed_count_++; |
| return result; |
| } |
| |
| bool QuicChromiumClientSession::HasNonMigratableStreams() const { |
| for (const auto& stream : dynamic_streams()) { |
| if (!static_cast<QuicChromiumClientStream*>(stream.second)->can_migrate()) |
| return true; |
| } |
| return false; |
| } |
| |
| void QuicChromiumClientSession::HandlePromised(QuicStreamId id, |
| QuicStreamId promised_id, |
| const SpdyHeaderBlock& headers) { |
| QuicClientSessionBase::HandlePromised(id, promised_id, headers); |
| net_log_.AddEvent(NetLog::TYPE_QUIC_SESSION_PUSH_PROMISE_RECEIVED, |
| base::Bind(&NetLogQuicPushPromiseReceivedCallback, &headers, |
| id, promised_id)); |
| } |
| |
| void QuicChromiumClientSession::DeletePromised( |
| QuicClientPromisedInfo* promised) { |
| if (IsOpenStream(promised->id())) |
| streams_pushed_and_claimed_count_++; |
| QuicClientSessionBase::DeletePromised(promised); |
| } |
| |
| } // namespace net |