1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/quic/quic_client_session.h"
7 #include "base/callback_helpers.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/metrics/histogram.h"
10 #include "base/metrics/sparse_histogram.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/values.h"
14 #include "net/base/io_buffer.h"
15 #include "net/base/net_errors.h"
16 #include "net/http/transport_security_state.h"
17 #include "net/quic/crypto/proof_verifier_chromium.h"
18 #include "net/quic/crypto/quic_server_info.h"
19 #include "net/quic/quic_connection_helper.h"
20 #include "net/quic/quic_crypto_client_stream_factory.h"
21 #include "net/quic/quic_default_packet_writer.h"
22 #include "net/quic/quic_server_id.h"
23 #include "net/quic/quic_stream_factory.h"
24 #include "net/spdy/spdy_session.h"
25 #include "net/ssl/channel_id_service.h"
26 #include "net/ssl/ssl_connection_status_flags.h"
27 #include "net/ssl/ssl_info.h"
28 #include "net/udp/datagram_client_socket.h"
34 // The length of time to wait for a 0-RTT handshake to complete
35 // before allowing the requests to possibly proceed over TCP.
36 const int k0RttHandshakeTimeoutMs = 300;
38 // Histograms for tracking down the crashes from http://crbug.com/354669
39 // Note: these values must be kept in sync with the corresponding values in:
40 // tools/metrics/histograms/histograms.xml
44 TRY_CREATE_STREAM = 2,
45 CREATE_OUTGOING_RELIABLE_STREAM = 3,
46 NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER = 4,
47 NOTIFY_FACTORY_OF_SESSION_CLOSED = 5,
51 void RecordUnexpectedOpenStreams(Location location) {
52 UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedOpenStreams", location,
56 void RecordUnexpectedObservers(Location location) {
57 UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedObservers", location,
61 void RecordUnexpectedNotGoingAway(Location location) {
62 UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedNotGoingAway", location,
66 // Histogram for recording the different reasons that a QUIC session is unable
67 // to complete the handshake.
68 enum HandshakeFailureReason {
69 HANDSHAKE_FAILURE_UNKNOWN = 0,
70 HANDSHAKE_FAILURE_BLACK_HOLE = 1,
71 HANDSHAKE_FAILURE_PUBLIC_RESET = 2,
72 NUM_HANDSHAKE_FAILURE_REASONS = 3,
75 void RecordHandshakeFailureReason(HandshakeFailureReason reason) {
76 UMA_HISTOGRAM_ENUMERATION(
77 "Net.QuicSession.ConnectionClose.HandshakeNotConfirmed.Reason",
78 reason, NUM_HANDSHAKE_FAILURE_REASONS);
81 // Note: these values must be kept in sync with the corresponding values in:
82 // tools/metrics/histograms/histograms.xml
85 STATE_ENCRYPTION_ESTABLISHED = 1,
86 STATE_HANDSHAKE_CONFIRMED = 2,
88 NUM_HANDSHAKE_STATES = 4
91 void RecordHandshakeState(HandshakeState state) {
92 UMA_HISTOGRAM_ENUMERATION("Net.QuicHandshakeState", state,
93 NUM_HANDSHAKE_STATES);
98 QuicClientSession::StreamRequest::StreamRequest() : stream_(NULL) {}
100 QuicClientSession::StreamRequest::~StreamRequest() {
104 int QuicClientSession::StreamRequest::StartRequest(
105 const base::WeakPtr<QuicClientSession>& session,
106 QuicReliableClientStream** stream,
107 const CompletionCallback& callback) {
110 int rv = session_->TryCreateStream(this, stream_);
111 if (rv == ERR_IO_PENDING) {
112 callback_ = callback;
118 void QuicClientSession::StreamRequest::CancelRequest() {
120 session_->CancelRequest(this);
125 void QuicClientSession::StreamRequest::OnRequestCompleteSuccess(
126 QuicReliableClientStream* stream) {
129 ResetAndReturn(&callback_).Run(OK);
132 void QuicClientSession::StreamRequest::OnRequestCompleteFailure(int rv) {
134 ResetAndReturn(&callback_).Run(rv);
137 QuicClientSession::QuicClientSession(
138 QuicConnection* connection,
139 scoped_ptr<DatagramClientSocket> socket,
140 scoped_ptr<QuicDefaultPacketWriter> writer,
141 QuicStreamFactory* stream_factory,
142 QuicCryptoClientStreamFactory* crypto_client_stream_factory,
143 TransportSecurityState* transport_security_state,
144 scoped_ptr<QuicServerInfo> server_info,
145 const QuicServerId& server_id,
146 const QuicConfig& config,
147 QuicCryptoClientConfig* crypto_config,
148 base::TaskRunner* task_runner,
150 : QuicClientSessionBase(connection, config),
151 server_host_port_(server_id.host_port_pair()),
152 require_confirmation_(false),
153 stream_factory_(stream_factory),
154 socket_(socket.Pass()),
155 writer_(writer.Pass()),
156 read_buffer_(new IOBufferWithSize(kMaxPacketSize)),
157 transport_security_state_(transport_security_state),
158 server_info_(server_info.Pass()),
159 read_pending_(false),
160 num_total_streams_(0),
161 task_runner_(task_runner),
162 net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_QUIC_SESSION)),
163 logger_(new QuicConnectionLogger(net_log_)),
164 num_packets_read_(0),
166 weak_factory_(this) {
167 crypto_stream_.reset(
168 crypto_client_stream_factory ?
169 crypto_client_stream_factory->CreateQuicCryptoClientStream(
170 server_id, this, crypto_config) :
171 new QuicCryptoClientStream(server_id, this,
172 new ProofVerifyContextChromium(net_log_),
175 connection->set_debug_visitor(logger_);
176 // TODO(rch): pass in full host port proxy pair
178 NetLog::TYPE_QUIC_SESSION,
179 NetLog::StringCallback("host", &server_id.host()));
182 QuicClientSession::~QuicClientSession() {
183 if (!streams()->empty())
184 RecordUnexpectedOpenStreams(DESTRUCTOR);
185 if (!observers_.empty())
186 RecordUnexpectedObservers(DESTRUCTOR);
188 RecordUnexpectedNotGoingAway(DESTRUCTOR);
190 while (!streams()->empty() ||
191 !observers_.empty() ||
192 !stream_requests_.empty()) {
193 // The session must be closed before it is destroyed.
194 DCHECK(streams()->empty());
195 CloseAllStreams(ERR_UNEXPECTED);
196 DCHECK(observers_.empty());
197 CloseAllObservers(ERR_UNEXPECTED);
199 connection()->set_debug_visitor(NULL);
200 net_log_.EndEvent(NetLog::TYPE_QUIC_SESSION);
202 while (!stream_requests_.empty()) {
203 StreamRequest* request = stream_requests_.front();
204 stream_requests_.pop_front();
205 request->OnRequestCompleteFailure(ERR_ABORTED);
209 if (connection()->connected()) {
210 // Ensure that the connection is closed by the time the session is
212 connection()->CloseConnection(QUIC_INTERNAL_ERROR, false);
215 if (IsEncryptionEstablished())
216 RecordHandshakeState(STATE_ENCRYPTION_ESTABLISHED);
217 if (IsCryptoHandshakeConfirmed())
218 RecordHandshakeState(STATE_HANDSHAKE_CONFIRMED);
220 RecordHandshakeState(STATE_FAILED);
222 UMA_HISTOGRAM_COUNTS("Net.QuicSession.NumTotalStreams", num_total_streams_);
223 UMA_HISTOGRAM_COUNTS("Net.QuicNumSentClientHellos",
224 crypto_stream_->num_sent_client_hellos());
225 if (!IsCryptoHandshakeConfirmed())
228 // Sending one client_hello means we had zero handshake-round-trips.
229 int round_trip_handshakes = crypto_stream_->num_sent_client_hellos() - 1;
231 // Don't bother with these histogram during tests, which mock out
232 // num_sent_client_hellos().
233 if (round_trip_handshakes < 0 || !stream_factory_)
236 bool port_selected = stream_factory_->enable_port_selection();
238 if (!GetSSLInfo(&ssl_info) || !ssl_info.cert) {
240 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectSelectPortForHTTP",
241 round_trip_handshakes, 0, 3, 4);
243 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectRandomPortForHTTP",
244 round_trip_handshakes, 0, 3, 4);
248 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectSelectPortForHTTPS",
249 round_trip_handshakes, 0, 3, 4);
251 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectRandomPortForHTTPS",
252 round_trip_handshakes, 0, 3, 4);
255 const QuicConnectionStats stats = connection()->GetStats();
256 if (stats.max_sequence_reordering == 0)
258 const uint64 kMaxReordering = 100;
259 uint64 reordering = kMaxReordering;
260 if (stats.min_rtt_us > 0 ) {
262 GG_UINT64_C(100) * stats.max_time_reordering_us / stats.min_rtt_us;
264 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.MaxReorderingTime",
265 reordering, 0, kMaxReordering, 50);
266 if (stats.min_rtt_us > 100 * 1000) {
267 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.MaxReorderingTimeLongRtt",
268 reordering, 0, kMaxReordering, 50);
270 UMA_HISTOGRAM_COUNTS("Net.QuicSession.MaxReordering",
271 stats.max_sequence_reordering);
274 void QuicClientSession::OnStreamFrames(
275 const std::vector<QuicStreamFrame>& frames) {
276 // Record total number of stream frames.
277 UMA_HISTOGRAM_COUNTS("Net.QuicNumStreamFramesInPacket", frames.size());
279 // Record number of frames per stream in packet.
280 typedef std::map<QuicStreamId, size_t> FrameCounter;
281 FrameCounter frames_per_stream;
282 for (size_t i = 0; i < frames.size(); ++i) {
283 frames_per_stream[frames[i].stream_id]++;
285 for (FrameCounter::const_iterator it = frames_per_stream.begin();
286 it != frames_per_stream.end(); ++it) {
287 UMA_HISTOGRAM_COUNTS("Net.QuicNumStreamFramesPerStreamInPacket",
291 return QuicSession::OnStreamFrames(frames);
294 void QuicClientSession::AddObserver(Observer* observer) {
296 RecordUnexpectedObservers(ADD_OBSERVER);
297 observer->OnSessionClosed(ERR_UNEXPECTED);
301 DCHECK(!ContainsKey(observers_, observer));
302 observers_.insert(observer);
305 void QuicClientSession::RemoveObserver(Observer* observer) {
306 DCHECK(ContainsKey(observers_, observer));
307 observers_.erase(observer);
310 int QuicClientSession::TryCreateStream(StreamRequest* request,
311 QuicReliableClientStream** stream) {
312 if (!crypto_stream_->encryption_established()) {
313 DLOG(DFATAL) << "Encryption not established.";
314 return ERR_CONNECTION_CLOSED;
317 if (goaway_received()) {
318 DVLOG(1) << "Going away.";
319 return ERR_CONNECTION_CLOSED;
322 if (!connection()->connected()) {
323 DVLOG(1) << "Already closed.";
324 return ERR_CONNECTION_CLOSED;
328 RecordUnexpectedOpenStreams(TRY_CREATE_STREAM);
329 return ERR_CONNECTION_CLOSED;
332 if (GetNumOpenStreams() < get_max_open_streams()) {
333 *stream = CreateOutgoingReliableStreamImpl();
337 stream_requests_.push_back(request);
338 return ERR_IO_PENDING;
341 void QuicClientSession::CancelRequest(StreamRequest* request) {
342 // Remove |request| from the queue while preserving the order of the
344 StreamRequestQueue::iterator it =
345 std::find(stream_requests_.begin(), stream_requests_.end(), request);
346 if (it != stream_requests_.end()) {
347 it = stream_requests_.erase(it);
351 QuicReliableClientStream* QuicClientSession::CreateOutgoingDataStream() {
352 if (!crypto_stream_->encryption_established()) {
353 DVLOG(1) << "Encryption not active so no outgoing stream created.";
356 if (GetNumOpenStreams() >= get_max_open_streams()) {
357 DVLOG(1) << "Failed to create a new outgoing stream. "
358 << "Already " << GetNumOpenStreams() << " open.";
361 if (goaway_received()) {
362 DVLOG(1) << "Failed to create a new outgoing stream. "
363 << "Already received goaway.";
367 RecordUnexpectedOpenStreams(CREATE_OUTGOING_RELIABLE_STREAM);
370 return CreateOutgoingReliableStreamImpl();
373 QuicReliableClientStream*
374 QuicClientSession::CreateOutgoingReliableStreamImpl() {
375 DCHECK(connection()->connected());
376 QuicReliableClientStream* stream =
377 new QuicReliableClientStream(GetNextStreamId(), this, net_log_);
378 ActivateStream(stream);
379 ++num_total_streams_;
380 UMA_HISTOGRAM_COUNTS("Net.QuicSession.NumOpenStreams", GetNumOpenStreams());
384 QuicCryptoClientStream* QuicClientSession::GetCryptoStream() {
385 return crypto_stream_.get();
388 // TODO(rtenneti): Add unittests for GetSSLInfo which exercise the various ways
389 // we learn about SSL info (sync vs async vs cached).
390 bool QuicClientSession::GetSSLInfo(SSLInfo* ssl_info) const {
392 if (!cert_verify_result_) {
396 ssl_info->cert_status = cert_verify_result_->cert_status;
397 ssl_info->cert = cert_verify_result_->verified_cert;
399 // TODO(wtc): Define QUIC "cipher suites".
400 // Report the TLS cipher suite that most closely resembles the crypto
401 // parameters of the QUIC connection.
402 QuicTag aead = crypto_stream_->crypto_negotiated_params().aead;
407 cipher_suite = 0xc02f; // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
411 cipher_suite = 0xcc13; // TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
418 int ssl_connection_status = 0;
419 ssl_connection_status |=
420 (cipher_suite & SSL_CONNECTION_CIPHERSUITE_MASK) <<
421 SSL_CONNECTION_CIPHERSUITE_SHIFT;
422 ssl_connection_status |=
423 (SSL_CONNECTION_VERSION_QUIC & SSL_CONNECTION_VERSION_MASK) <<
424 SSL_CONNECTION_VERSION_SHIFT;
426 ssl_info->public_key_hashes = cert_verify_result_->public_key_hashes;
427 ssl_info->is_issued_by_known_root =
428 cert_verify_result_->is_issued_by_known_root;
430 ssl_info->connection_status = ssl_connection_status;
431 ssl_info->client_cert_sent = false;
432 ssl_info->channel_id_sent = crypto_stream_->WasChannelIDSent();
433 ssl_info->security_bits = security_bits;
434 ssl_info->handshake_type = SSLInfo::HANDSHAKE_FULL;
435 ssl_info->pinning_failure_log = pinning_failure_log_;
439 int QuicClientSession::CryptoConnect(bool require_confirmation,
440 const CompletionCallback& callback) {
441 require_confirmation_ = require_confirmation;
442 handshake_start_ = base::TimeTicks::Now();
443 RecordHandshakeState(STATE_STARTED);
444 DCHECK(flow_controller());
445 if (!crypto_stream_->CryptoConnect()) {
446 // TODO(wtc): change crypto_stream_.CryptoConnect() to return a
447 // QuicErrorCode and map it to a net error code.
448 return ERR_CONNECTION_FAILED;
451 if (IsCryptoHandshakeConfirmed())
454 // Unless we require handshake confirmation, activate the session if
455 // we have established initial encryption.
456 if (!require_confirmation_ && IsEncryptionEstablished()) {
457 // To mitigate the effects of hanging 0-RTT connections, set up a timer to
458 // cancel any requests, if the handshake takes too long.
459 task_runner_->PostDelayedTask(
461 base::Bind(&QuicClientSession::OnConnectTimeout,
462 weak_factory_.GetWeakPtr()),
463 base::TimeDelta::FromMilliseconds(k0RttHandshakeTimeoutMs));
468 callback_ = callback;
469 return ERR_IO_PENDING;
472 int QuicClientSession::ResumeCryptoConnect(const CompletionCallback& callback) {
474 if (IsCryptoHandshakeConfirmed())
477 if (!connection()->connected())
478 return ERR_QUIC_HANDSHAKE_FAILED;
480 callback_ = callback;
481 return ERR_IO_PENDING;
484 int QuicClientSession::GetNumSentClientHellos() const {
485 return crypto_stream_->num_sent_client_hellos();
488 bool QuicClientSession::CanPool(const std::string& hostname) const {
489 DCHECK(connection()->connected());
491 if (!GetSSLInfo(&ssl_info) || !ssl_info.cert) {
492 // We can always pool with insecure QUIC sessions.
496 return SpdySession::CanPool(transport_security_state_, ssl_info,
497 server_host_port_.host(), hostname);
500 QuicDataStream* QuicClientSession::CreateIncomingDataStream(
502 DLOG(ERROR) << "Server push not supported";
506 void QuicClientSession::CloseStream(QuicStreamId stream_id) {
507 ReliableQuicStream* stream = GetStream(stream_id);
509 logger_->UpdateReceivedFrameCounts(
510 stream_id, stream->num_frames_received(),
511 stream->num_duplicate_frames_received());
513 QuicSession::CloseStream(stream_id);
517 void QuicClientSession::SendRstStream(QuicStreamId id,
518 QuicRstStreamErrorCode error,
519 QuicStreamOffset bytes_written) {
520 QuicSession::SendRstStream(id, error, bytes_written);
524 void QuicClientSession::OnClosedStream() {
525 if (GetNumOpenStreams() < get_max_open_streams() &&
526 !stream_requests_.empty() &&
527 crypto_stream_->encryption_established() &&
528 !goaway_received() &&
530 connection()->connected()) {
531 StreamRequest* request = stream_requests_.front();
532 stream_requests_.pop_front();
533 request->OnRequestCompleteSuccess(CreateOutgoingReliableStreamImpl());
536 if (GetNumOpenStreams() == 0) {
537 stream_factory_->OnIdleSession(this);
541 void QuicClientSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
542 if (!callback_.is_null() &&
543 (!require_confirmation_ || event == HANDSHAKE_CONFIRMED)) {
544 // TODO(rtenneti): Currently for all CryptoHandshakeEvent events, callback_
545 // could be called because there are no error events in CryptoHandshakeEvent
546 // enum. If error events are added to CryptoHandshakeEvent, then the
547 // following code needs to changed.
548 base::ResetAndReturn(&callback_).Run(OK);
550 if (event == HANDSHAKE_CONFIRMED) {
551 UMA_HISTOGRAM_TIMES("Net.QuicSession.HandshakeConfirmedTime",
552 base::TimeTicks::Now() - handshake_start_);
553 ObserverSet::iterator it = observers_.begin();
554 while (it != observers_.end()) {
555 Observer* observer = *it;
557 observer->OnCryptoHandshakeConfirmed();
560 QuicSession::OnCryptoHandshakeEvent(event);
563 void QuicClientSession::OnCryptoHandshakeMessageSent(
564 const CryptoHandshakeMessage& message) {
565 logger_->OnCryptoHandshakeMessageSent(message);
568 void QuicClientSession::OnCryptoHandshakeMessageReceived(
569 const CryptoHandshakeMessage& message) {
570 logger_->OnCryptoHandshakeMessageReceived(message);
573 void QuicClientSession::OnConnectionClosed(QuicErrorCode error,
575 DCHECK(!connection()->connected());
576 logger_->OnConnectionClosed(error, from_peer);
578 UMA_HISTOGRAM_SPARSE_SLOWLY(
579 "Net.QuicSession.ConnectionCloseErrorCodeServer", error);
581 UMA_HISTOGRAM_SPARSE_SLOWLY(
582 "Net.QuicSession.ConnectionCloseErrorCodeClient", error);
585 if (error == QUIC_CONNECTION_TIMED_OUT) {
586 UMA_HISTOGRAM_COUNTS(
587 "Net.QuicSession.ConnectionClose.NumOpenStreams.TimedOut",
588 GetNumOpenStreams());
589 if (!IsCryptoHandshakeConfirmed()) {
590 UMA_HISTOGRAM_COUNTS(
591 "Net.QuicSession.ConnectionClose.NumOpenStreams.HandshakeTimedOut",
592 GetNumOpenStreams());
593 UMA_HISTOGRAM_COUNTS(
594 "Net.QuicSession.ConnectionClose.NumTotalStreams.HandshakeTimedOut",
599 if (!IsCryptoHandshakeConfirmed()) {
600 if (error == QUIC_PUBLIC_RESET) {
601 RecordHandshakeFailureReason(HANDSHAKE_FAILURE_PUBLIC_RESET);
602 } else if (connection()->GetStats().packets_received == 0) {
603 RecordHandshakeFailureReason(HANDSHAKE_FAILURE_BLACK_HOLE);
604 UMA_HISTOGRAM_SPARSE_SLOWLY(
605 "Net.QuicSession.ConnectionClose.HandshakeFailureBlackHole.QuicError",
608 RecordHandshakeFailureReason(HANDSHAKE_FAILURE_UNKNOWN);
609 UMA_HISTOGRAM_SPARSE_SLOWLY(
610 "Net.QuicSession.ConnectionClose.HandshakeFailureUnknown.QuicError",
615 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.QuicVersion",
616 connection()->version());
617 NotifyFactoryOfSessionGoingAway();
618 if (!callback_.is_null()) {
619 base::ResetAndReturn(&callback_).Run(ERR_QUIC_PROTOCOL_ERROR);
622 QuicSession::OnConnectionClosed(error, from_peer);
623 DCHECK(streams()->empty());
624 CloseAllStreams(ERR_UNEXPECTED);
625 CloseAllObservers(ERR_UNEXPECTED);
626 NotifyFactoryOfSessionClosedLater();
629 void QuicClientSession::OnSuccessfulVersionNegotiation(
630 const QuicVersion& version) {
631 logger_->OnSuccessfulVersionNegotiation(version);
632 QuicSession::OnSuccessfulVersionNegotiation(version);
635 void QuicClientSession::OnProofValid(
636 const QuicCryptoClientConfig::CachedState& cached) {
637 DCHECK(cached.proof_valid());
639 if (!server_info_ || !server_info_->IsReadyToPersist()) {
643 QuicServerInfo::State* state = server_info_->mutable_state();
645 state->server_config = cached.server_config();
646 state->source_address_token = cached.source_address_token();
647 state->server_config_sig = cached.signature();
648 state->certs = cached.certs();
650 server_info_->Persist();
653 void QuicClientSession::OnProofVerifyDetailsAvailable(
654 const ProofVerifyDetails& verify_details) {
655 const ProofVerifyDetailsChromium* verify_details_chromium =
656 reinterpret_cast<const ProofVerifyDetailsChromium*>(&verify_details);
657 CertVerifyResult* result_copy = new CertVerifyResult;
658 result_copy->CopyFrom(verify_details_chromium->cert_verify_result);
659 cert_verify_result_.reset(result_copy);
660 pinning_failure_log_ = verify_details_chromium->pinning_failure_log;
661 logger_->OnCertificateVerified(*cert_verify_result_);
664 void QuicClientSession::StartReading() {
668 read_pending_ = true;
669 int rv = socket_->Read(read_buffer_.get(),
670 read_buffer_->size(),
671 base::Bind(&QuicClientSession::OnReadComplete,
672 weak_factory_.GetWeakPtr()));
673 if (rv == ERR_IO_PENDING) {
674 num_packets_read_ = 0;
678 if (++num_packets_read_ > 32) {
679 num_packets_read_ = 0;
680 // Data was read, process it.
681 // Schedule the work through the message loop to 1) prevent infinite
682 // recursion and 2) avoid blocking the thread for too long.
683 base::MessageLoop::current()->PostTask(
685 base::Bind(&QuicClientSession::OnReadComplete,
686 weak_factory_.GetWeakPtr(), rv));
692 void QuicClientSession::CloseSessionOnError(int error) {
693 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.CloseSessionOnError", -error);
694 CloseSessionOnErrorInner(error, QUIC_INTERNAL_ERROR);
695 NotifyFactoryOfSessionClosed();
698 void QuicClientSession::CloseSessionOnErrorInner(int net_error,
699 QuicErrorCode quic_error) {
700 if (!callback_.is_null()) {
701 base::ResetAndReturn(&callback_).Run(net_error);
703 CloseAllStreams(net_error);
704 CloseAllObservers(net_error);
706 NetLog::TYPE_QUIC_SESSION_CLOSE_ON_ERROR,
707 NetLog::IntegerCallback("net_error", net_error));
709 if (connection()->connected())
710 connection()->CloseConnection(quic_error, false);
711 DCHECK(!connection()->connected());
714 void QuicClientSession::CloseAllStreams(int net_error) {
715 while (!streams()->empty()) {
716 ReliableQuicStream* stream = streams()->begin()->second;
717 QuicStreamId id = stream->id();
718 static_cast<QuicReliableClientStream*>(stream)->OnError(net_error);
723 void QuicClientSession::CloseAllObservers(int net_error) {
724 while (!observers_.empty()) {
725 Observer* observer = *observers_.begin();
726 observers_.erase(observer);
727 observer->OnSessionClosed(net_error);
731 base::Value* QuicClientSession::GetInfoAsValue(
732 const std::set<HostPortPair>& aliases) {
733 base::DictionaryValue* dict = new base::DictionaryValue();
734 dict->SetString("version", QuicVersionToString(connection()->version()));
735 dict->SetInteger("open_streams", GetNumOpenStreams());
736 base::ListValue* stream_list = new base::ListValue();
737 for (base::hash_map<QuicStreamId, QuicDataStream*>::const_iterator it
738 = streams()->begin();
739 it != streams()->end();
741 stream_list->Append(new base::StringValue(
742 base::Uint64ToString(it->second->id())));
744 dict->Set("active_streams", stream_list);
746 dict->SetInteger("total_streams", num_total_streams_);
747 dict->SetString("peer_address", peer_address().ToString());
748 dict->SetString("connection_id", base::Uint64ToString(connection_id()));
749 dict->SetBoolean("connected", connection()->connected());
750 const QuicConnectionStats& stats = connection()->GetStats();
751 dict->SetInteger("packets_sent", stats.packets_sent);
752 dict->SetInteger("packets_received", stats.packets_received);
753 dict->SetInteger("packets_lost", stats.packets_lost);
755 dict->SetBoolean("secure", GetSSLInfo(&ssl_info) && ssl_info.cert);
757 base::ListValue* alias_list = new base::ListValue();
758 for (std::set<HostPortPair>::const_iterator it = aliases.begin();
759 it != aliases.end(); it++) {
760 alias_list->Append(new base::StringValue(it->ToString()));
762 dict->Set("aliases", alias_list);
767 base::WeakPtr<QuicClientSession> QuicClientSession::GetWeakPtr() {
768 return weak_factory_.GetWeakPtr();
771 void QuicClientSession::OnReadComplete(int result) {
772 read_pending_ = false;
774 result = ERR_CONNECTION_CLOSED;
777 DVLOG(1) << "Closing session on read error: " << result;
778 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.ReadError", -result);
779 NotifyFactoryOfSessionGoingAway();
780 CloseSessionOnErrorInner(result, QUIC_PACKET_READ_ERROR);
781 NotifyFactoryOfSessionClosedLater();
785 QuicEncryptedPacket packet(read_buffer_->data(), result);
786 IPEndPoint local_address;
787 IPEndPoint peer_address;
788 socket_->GetLocalAddress(&local_address);
789 socket_->GetPeerAddress(&peer_address);
790 // ProcessUdpPacket might result in |this| being deleted, so we
791 // use a weak pointer to be safe.
792 connection()->ProcessUdpPacket(local_address, peer_address, packet);
793 if (!connection()->connected()) {
794 NotifyFactoryOfSessionClosedLater();
800 void QuicClientSession::NotifyFactoryOfSessionGoingAway() {
803 stream_factory_->OnSessionGoingAway(this);
806 void QuicClientSession::NotifyFactoryOfSessionClosedLater() {
807 if (!streams()->empty())
808 RecordUnexpectedOpenStreams(NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER);
811 RecordUnexpectedNotGoingAway(NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER);
814 DCHECK_EQ(0u, GetNumOpenStreams());
815 DCHECK(!connection()->connected());
816 base::MessageLoop::current()->PostTask(
818 base::Bind(&QuicClientSession::NotifyFactoryOfSessionClosed,
819 weak_factory_.GetWeakPtr()));
822 void QuicClientSession::NotifyFactoryOfSessionClosed() {
823 if (!streams()->empty())
824 RecordUnexpectedOpenStreams(NOTIFY_FACTORY_OF_SESSION_CLOSED);
827 RecordUnexpectedNotGoingAway(NOTIFY_FACTORY_OF_SESSION_CLOSED);
830 DCHECK_EQ(0u, GetNumOpenStreams());
831 // Will delete |this|.
833 stream_factory_->OnSessionClosed(this);
836 void QuicClientSession::OnConnectTimeout() {
837 DCHECK(callback_.is_null());
838 DCHECK(IsEncryptionEstablished());
840 if (IsCryptoHandshakeConfirmed())
843 // TODO(rch): re-enable this code once beta is cut.
844 // if (stream_factory_)
845 // stream_factory_->OnSessionConnectTimeout(this);
846 // CloseAllStreams(ERR_QUIC_HANDSHAKE_FAILED);
847 // DCHECK_EQ(0u, GetNumOpenStreams());