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_server_id.h"
22 #include "net/quic/quic_stream_factory.h"
23 #include "net/spdy/spdy_session.h"
24 #include "net/ssl/channel_id_service.h"
25 #include "net/ssl/ssl_connection_status_flags.h"
26 #include "net/ssl/ssl_info.h"
27 #include "net/udp/datagram_client_socket.h"
33 // The length of time to wait for a 0-RTT handshake to complete
34 // before allowing the requests to possibly proceed over TCP.
35 const int k0RttHandshakeTimeoutMs
= 300;
37 // Histograms for tracking down the crashes from http://crbug.com/354669
38 // Note: these values must be kept in sync with the corresponding values in:
39 // tools/metrics/histograms/histograms.xml
43 TRY_CREATE_STREAM
= 2,
44 CREATE_OUTGOING_RELIABLE_STREAM
= 3,
45 NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER
= 4,
46 NOTIFY_FACTORY_OF_SESSION_CLOSED
= 5,
50 void RecordUnexpectedOpenStreams(Location location
) {
51 UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedOpenStreams", location
,
55 void RecordUnexpectedObservers(Location location
) {
56 UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedObservers", location
,
60 void RecordUnexpectedNotGoingAway(Location location
) {
61 UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedNotGoingAway", location
,
65 // Histogram for recording the different reasons that a QUIC session is unable
66 // to complete the handshake.
67 enum HandshakeFailureReason
{
68 HANDSHAKE_FAILURE_UNKNOWN
= 0,
69 HANDSHAKE_FAILURE_BLACK_HOLE
= 1,
70 HANDSHAKE_FAILURE_PUBLIC_RESET
= 2,
71 NUM_HANDSHAKE_FAILURE_REASONS
= 3,
74 void RecordHandshakeFailureReason(HandshakeFailureReason reason
) {
75 UMA_HISTOGRAM_ENUMERATION(
76 "Net.QuicSession.ConnectionClose.HandshakeNotConfirmed.Reason",
77 reason
, NUM_HANDSHAKE_FAILURE_REASONS
);
80 // Note: these values must be kept in sync with the corresponding values in:
81 // tools/metrics/histograms/histograms.xml
84 STATE_ENCRYPTION_ESTABLISHED
= 1,
85 STATE_HANDSHAKE_CONFIRMED
= 2,
87 NUM_HANDSHAKE_STATES
= 4
90 void RecordHandshakeState(HandshakeState state
) {
91 UMA_HISTOGRAM_ENUMERATION("Net.QuicHandshakeState", state
,
92 NUM_HANDSHAKE_STATES
);
97 QuicClientSession::StreamRequest::StreamRequest() : stream_(NULL
) {}
99 QuicClientSession::StreamRequest::~StreamRequest() {
103 int QuicClientSession::StreamRequest::StartRequest(
104 const base::WeakPtr
<QuicClientSession
>& session
,
105 QuicReliableClientStream
** stream
,
106 const CompletionCallback
& callback
) {
109 int rv
= session_
->TryCreateStream(this, stream_
);
110 if (rv
== ERR_IO_PENDING
) {
111 callback_
= callback
;
117 void QuicClientSession::StreamRequest::CancelRequest() {
119 session_
->CancelRequest(this);
124 void QuicClientSession::StreamRequest::OnRequestCompleteSuccess(
125 QuicReliableClientStream
* stream
) {
128 ResetAndReturn(&callback_
).Run(OK
);
131 void QuicClientSession::StreamRequest::OnRequestCompleteFailure(int rv
) {
133 ResetAndReturn(&callback_
).Run(rv
);
136 QuicClientSession::QuicClientSession(
137 QuicConnection
* connection
,
138 scoped_ptr
<DatagramClientSocket
> socket
,
139 QuicStreamFactory
* stream_factory
,
140 TransportSecurityState
* transport_security_state
,
141 scoped_ptr
<QuicServerInfo
> server_info
,
142 const QuicConfig
& config
,
143 base::TaskRunner
* task_runner
,
145 : QuicClientSessionBase(connection
, config
),
146 require_confirmation_(false),
147 stream_factory_(stream_factory
),
148 socket_(socket
.Pass()),
149 read_buffer_(new IOBufferWithSize(kMaxPacketSize
)),
150 transport_security_state_(transport_security_state
),
151 server_info_(server_info
.Pass()),
152 read_pending_(false),
153 num_total_streams_(0),
154 task_runner_(task_runner
),
155 net_log_(BoundNetLog::Make(net_log
, NetLog::SOURCE_QUIC_SESSION
)),
156 logger_(new QuicConnectionLogger(net_log_
)),
157 num_packets_read_(0),
159 weak_factory_(this) {
160 connection
->set_debug_visitor(logger_
);
163 void QuicClientSession::InitializeSession(
164 const QuicServerId
& server_id
,
165 QuicCryptoClientConfig
* crypto_config
,
166 QuicCryptoClientStreamFactory
* crypto_client_stream_factory
) {
167 server_host_port_
= server_id
.host_port_pair();
168 crypto_stream_
.reset(
169 crypto_client_stream_factory
?
170 crypto_client_stream_factory
->CreateQuicCryptoClientStream(
171 server_id
, this, crypto_config
) :
172 new QuicCryptoClientStream(server_id
, this,
173 new ProofVerifyContextChromium(net_log_
),
175 QuicClientSessionBase::InitializeSession();
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
.get()) {
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);
245 if (require_confirmation_
) {
246 UMA_HISTOGRAM_CUSTOM_COUNTS(
247 "Net.QuicSession.ConnectRandomPortRequiringConfirmationForHTTP",
248 round_trip_handshakes
, 0, 3, 4);
253 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectSelectPortForHTTPS",
254 round_trip_handshakes
, 0, 3, 4);
256 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectRandomPortForHTTPS",
257 round_trip_handshakes
, 0, 3, 4);
258 if (require_confirmation_
) {
259 UMA_HISTOGRAM_CUSTOM_COUNTS(
260 "Net.QuicSession.ConnectRandomPortRequiringConfirmationForHTTPS",
261 round_trip_handshakes
, 0, 3, 4);
265 const QuicConnectionStats stats
= connection()->GetStats();
266 if (stats
.max_sequence_reordering
== 0)
268 const uint64 kMaxReordering
= 100;
269 uint64 reordering
= kMaxReordering
;
270 if (stats
.min_rtt_us
> 0 ) {
272 GG_UINT64_C(100) * stats
.max_time_reordering_us
/ stats
.min_rtt_us
;
274 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.MaxReorderingTime",
275 reordering
, 0, kMaxReordering
, 50);
276 if (stats
.min_rtt_us
> 100 * 1000) {
277 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.MaxReorderingTimeLongRtt",
278 reordering
, 0, kMaxReordering
, 50);
280 UMA_HISTOGRAM_COUNTS("Net.QuicSession.MaxReordering",
281 stats
.max_sequence_reordering
);
284 void QuicClientSession::OnStreamFrames(
285 const std::vector
<QuicStreamFrame
>& frames
) {
286 // Record total number of stream frames.
287 UMA_HISTOGRAM_COUNTS("Net.QuicNumStreamFramesInPacket", frames
.size());
289 // Record number of frames per stream in packet.
290 typedef std::map
<QuicStreamId
, size_t> FrameCounter
;
291 FrameCounter frames_per_stream
;
292 for (size_t i
= 0; i
< frames
.size(); ++i
) {
293 frames_per_stream
[frames
[i
].stream_id
]++;
295 for (FrameCounter::const_iterator it
= frames_per_stream
.begin();
296 it
!= frames_per_stream
.end(); ++it
) {
297 UMA_HISTOGRAM_COUNTS("Net.QuicNumStreamFramesPerStreamInPacket",
301 return QuicSession::OnStreamFrames(frames
);
304 void QuicClientSession::AddObserver(Observer
* observer
) {
306 RecordUnexpectedObservers(ADD_OBSERVER
);
307 observer
->OnSessionClosed(ERR_UNEXPECTED
);
311 DCHECK(!ContainsKey(observers_
, observer
));
312 observers_
.insert(observer
);
315 void QuicClientSession::RemoveObserver(Observer
* observer
) {
316 DCHECK(ContainsKey(observers_
, observer
));
317 observers_
.erase(observer
);
320 int QuicClientSession::TryCreateStream(StreamRequest
* request
,
321 QuicReliableClientStream
** stream
) {
322 if (!crypto_stream_
->encryption_established()) {
323 DLOG(DFATAL
) << "Encryption not established.";
324 return ERR_CONNECTION_CLOSED
;
327 if (goaway_received()) {
328 DVLOG(1) << "Going away.";
329 return ERR_CONNECTION_CLOSED
;
332 if (!connection()->connected()) {
333 DVLOG(1) << "Already closed.";
334 return ERR_CONNECTION_CLOSED
;
338 RecordUnexpectedOpenStreams(TRY_CREATE_STREAM
);
339 return ERR_CONNECTION_CLOSED
;
342 if (GetNumOpenStreams() < get_max_open_streams()) {
343 *stream
= CreateOutgoingReliableStreamImpl();
347 stream_requests_
.push_back(request
);
348 return ERR_IO_PENDING
;
351 void QuicClientSession::CancelRequest(StreamRequest
* request
) {
352 // Remove |request| from the queue while preserving the order of the
354 StreamRequestQueue::iterator it
=
355 std::find(stream_requests_
.begin(), stream_requests_
.end(), request
);
356 if (it
!= stream_requests_
.end()) {
357 it
= stream_requests_
.erase(it
);
361 QuicReliableClientStream
* QuicClientSession::CreateOutgoingDataStream() {
362 if (!crypto_stream_
->encryption_established()) {
363 DVLOG(1) << "Encryption not active so no outgoing stream created.";
366 if (GetNumOpenStreams() >= get_max_open_streams()) {
367 DVLOG(1) << "Failed to create a new outgoing stream. "
368 << "Already " << GetNumOpenStreams() << " open.";
371 if (goaway_received()) {
372 DVLOG(1) << "Failed to create a new outgoing stream. "
373 << "Already received goaway.";
377 RecordUnexpectedOpenStreams(CREATE_OUTGOING_RELIABLE_STREAM
);
380 return CreateOutgoingReliableStreamImpl();
383 QuicReliableClientStream
*
384 QuicClientSession::CreateOutgoingReliableStreamImpl() {
385 DCHECK(connection()->connected());
386 QuicReliableClientStream
* stream
=
387 new QuicReliableClientStream(GetNextStreamId(), this, net_log_
);
388 ActivateStream(stream
);
389 ++num_total_streams_
;
390 UMA_HISTOGRAM_COUNTS("Net.QuicSession.NumOpenStreams", GetNumOpenStreams());
394 QuicCryptoClientStream
* QuicClientSession::GetCryptoStream() {
395 return crypto_stream_
.get();
398 // TODO(rtenneti): Add unittests for GetSSLInfo which exercise the various ways
399 // we learn about SSL info (sync vs async vs cached).
400 bool QuicClientSession::GetSSLInfo(SSLInfo
* ssl_info
) const {
402 if (!cert_verify_result_
) {
406 ssl_info
->cert_status
= cert_verify_result_
->cert_status
;
407 ssl_info
->cert
= cert_verify_result_
->verified_cert
;
409 // TODO(wtc): Define QUIC "cipher suites".
410 // Report the TLS cipher suite that most closely resembles the crypto
411 // parameters of the QUIC connection.
412 QuicTag aead
= crypto_stream_
->crypto_negotiated_params().aead
;
417 cipher_suite
= 0xc02f; // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
421 cipher_suite
= 0xcc13; // TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
428 int ssl_connection_status
= 0;
429 ssl_connection_status
|=
430 (cipher_suite
& SSL_CONNECTION_CIPHERSUITE_MASK
) <<
431 SSL_CONNECTION_CIPHERSUITE_SHIFT
;
432 ssl_connection_status
|=
433 (SSL_CONNECTION_VERSION_QUIC
& SSL_CONNECTION_VERSION_MASK
) <<
434 SSL_CONNECTION_VERSION_SHIFT
;
436 ssl_info
->public_key_hashes
= cert_verify_result_
->public_key_hashes
;
437 ssl_info
->is_issued_by_known_root
=
438 cert_verify_result_
->is_issued_by_known_root
;
440 ssl_info
->connection_status
= ssl_connection_status
;
441 ssl_info
->client_cert_sent
= false;
442 ssl_info
->channel_id_sent
= crypto_stream_
->WasChannelIDSent();
443 ssl_info
->security_bits
= security_bits
;
444 ssl_info
->handshake_type
= SSLInfo::HANDSHAKE_FULL
;
445 ssl_info
->pinning_failure_log
= pinning_failure_log_
;
449 int QuicClientSession::CryptoConnect(bool require_confirmation
,
450 const CompletionCallback
& callback
) {
451 require_confirmation_
= require_confirmation
;
452 handshake_start_
= base::TimeTicks::Now();
453 RecordHandshakeState(STATE_STARTED
);
454 DCHECK(flow_controller());
455 if (!crypto_stream_
->CryptoConnect()) {
456 // TODO(wtc): change crypto_stream_.CryptoConnect() to return a
457 // QuicErrorCode and map it to a net error code.
458 return ERR_CONNECTION_FAILED
;
461 if (IsCryptoHandshakeConfirmed())
464 // Unless we require handshake confirmation, activate the session if
465 // we have established initial encryption.
466 if (!require_confirmation_
&& IsEncryptionEstablished()) {
467 // To mitigate the effects of hanging 0-RTT connections, set up a timer to
468 // cancel any requests, if the handshake takes too long.
469 task_runner_
->PostDelayedTask(
471 base::Bind(&QuicClientSession::OnConnectTimeout
,
472 weak_factory_
.GetWeakPtr()),
473 base::TimeDelta::FromMilliseconds(k0RttHandshakeTimeoutMs
));
478 callback_
= callback
;
479 return ERR_IO_PENDING
;
482 int QuicClientSession::ResumeCryptoConnect(const CompletionCallback
& callback
) {
484 if (IsCryptoHandshakeConfirmed())
487 if (!connection()->connected())
488 return ERR_QUIC_HANDSHAKE_FAILED
;
490 callback_
= callback
;
491 return ERR_IO_PENDING
;
494 int QuicClientSession::GetNumSentClientHellos() const {
495 return crypto_stream_
->num_sent_client_hellos();
498 bool QuicClientSession::CanPool(const std::string
& hostname
) const {
499 DCHECK(connection()->connected());
501 if (!GetSSLInfo(&ssl_info
) || !ssl_info
.cert
.get()) {
502 // We can always pool with insecure QUIC sessions.
506 return SpdySession::CanPool(transport_security_state_
, ssl_info
,
507 server_host_port_
.host(), hostname
);
510 QuicDataStream
* QuicClientSession::CreateIncomingDataStream(
512 DLOG(ERROR
) << "Server push not supported";
516 void QuicClientSession::CloseStream(QuicStreamId stream_id
) {
517 ReliableQuicStream
* stream
= GetStream(stream_id
);
519 logger_
->UpdateReceivedFrameCounts(
520 stream_id
, stream
->num_frames_received(),
521 stream
->num_duplicate_frames_received());
523 QuicSession::CloseStream(stream_id
);
527 void QuicClientSession::SendRstStream(QuicStreamId id
,
528 QuicRstStreamErrorCode error
,
529 QuicStreamOffset bytes_written
) {
530 QuicSession::SendRstStream(id
, error
, bytes_written
);
534 void QuicClientSession::OnClosedStream() {
535 if (GetNumOpenStreams() < get_max_open_streams() &&
536 !stream_requests_
.empty() &&
537 crypto_stream_
->encryption_established() &&
538 !goaway_received() &&
540 connection()->connected()) {
541 StreamRequest
* request
= stream_requests_
.front();
542 stream_requests_
.pop_front();
543 request
->OnRequestCompleteSuccess(CreateOutgoingReliableStreamImpl());
546 if (GetNumOpenStreams() == 0) {
547 stream_factory_
->OnIdleSession(this);
551 void QuicClientSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event
) {
552 if (!callback_
.is_null() &&
553 (!require_confirmation_
|| event
== HANDSHAKE_CONFIRMED
)) {
554 // TODO(rtenneti): Currently for all CryptoHandshakeEvent events, callback_
555 // could be called because there are no error events in CryptoHandshakeEvent
556 // enum. If error events are added to CryptoHandshakeEvent, then the
557 // following code needs to changed.
558 base::ResetAndReturn(&callback_
).Run(OK
);
560 if (event
== HANDSHAKE_CONFIRMED
) {
561 UMA_HISTOGRAM_TIMES("Net.QuicSession.HandshakeConfirmedTime",
562 base::TimeTicks::Now() - handshake_start_
);
563 ObserverSet::iterator it
= observers_
.begin();
564 while (it
!= observers_
.end()) {
565 Observer
* observer
= *it
;
567 observer
->OnCryptoHandshakeConfirmed();
570 QuicSession::OnCryptoHandshakeEvent(event
);
573 void QuicClientSession::OnCryptoHandshakeMessageSent(
574 const CryptoHandshakeMessage
& message
) {
575 logger_
->OnCryptoHandshakeMessageSent(message
);
578 void QuicClientSession::OnCryptoHandshakeMessageReceived(
579 const CryptoHandshakeMessage
& message
) {
580 logger_
->OnCryptoHandshakeMessageReceived(message
);
583 void QuicClientSession::OnConnectionClosed(QuicErrorCode error
,
585 DCHECK(!connection()->connected());
586 logger_
->OnConnectionClosed(error
, from_peer
);
588 UMA_HISTOGRAM_SPARSE_SLOWLY(
589 "Net.QuicSession.ConnectionCloseErrorCodeServer", error
);
591 UMA_HISTOGRAM_SPARSE_SLOWLY(
592 "Net.QuicSession.ConnectionCloseErrorCodeClient", error
);
595 if (error
== QUIC_CONNECTION_TIMED_OUT
) {
596 UMA_HISTOGRAM_COUNTS(
597 "Net.QuicSession.ConnectionClose.NumOpenStreams.TimedOut",
598 GetNumOpenStreams());
599 if (IsCryptoHandshakeConfirmed()) {
600 if (GetNumOpenStreams() > 0) {
601 UMA_HISTOGRAM_BOOLEAN(
602 "Net.QuicSession.TimedOutWithOpenStreams.HasUnackedPackets",
603 connection()->sent_packet_manager().HasUnackedPackets());
604 UMA_HISTOGRAM_COUNTS(
605 "Net.QuicSession.TimedOutWithOpenStreams.ConsecutiveRTOCount",
606 connection()->sent_packet_manager().consecutive_rto_count());
607 UMA_HISTOGRAM_COUNTS(
608 "Net.QuicSession.TimedOutWithOpenStreams.ConsecutiveTLPCount",
609 connection()->sent_packet_manager().consecutive_tlp_count());
612 UMA_HISTOGRAM_COUNTS(
613 "Net.QuicSession.ConnectionClose.NumOpenStreams.HandshakeTimedOut",
614 GetNumOpenStreams());
615 UMA_HISTOGRAM_COUNTS(
616 "Net.QuicSession.ConnectionClose.NumTotalStreams.HandshakeTimedOut",
621 if (!IsCryptoHandshakeConfirmed()) {
622 if (error
== QUIC_PUBLIC_RESET
) {
623 RecordHandshakeFailureReason(HANDSHAKE_FAILURE_PUBLIC_RESET
);
624 } else if (connection()->GetStats().packets_received
== 0) {
625 RecordHandshakeFailureReason(HANDSHAKE_FAILURE_BLACK_HOLE
);
626 UMA_HISTOGRAM_SPARSE_SLOWLY(
627 "Net.QuicSession.ConnectionClose.HandshakeFailureBlackHole.QuicError",
630 RecordHandshakeFailureReason(HANDSHAKE_FAILURE_UNKNOWN
);
631 UMA_HISTOGRAM_SPARSE_SLOWLY(
632 "Net.QuicSession.ConnectionClose.HandshakeFailureUnknown.QuicError",
637 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.QuicVersion",
638 connection()->version());
639 NotifyFactoryOfSessionGoingAway();
640 if (!callback_
.is_null()) {
641 base::ResetAndReturn(&callback_
).Run(ERR_QUIC_PROTOCOL_ERROR
);
644 QuicSession::OnConnectionClosed(error
, from_peer
);
645 DCHECK(streams()->empty());
646 CloseAllStreams(ERR_UNEXPECTED
);
647 CloseAllObservers(ERR_UNEXPECTED
);
648 NotifyFactoryOfSessionClosedLater();
651 void QuicClientSession::OnSuccessfulVersionNegotiation(
652 const QuicVersion
& version
) {
653 logger_
->OnSuccessfulVersionNegotiation(version
);
654 QuicSession::OnSuccessfulVersionNegotiation(version
);
657 void QuicClientSession::OnProofValid(
658 const QuicCryptoClientConfig::CachedState
& cached
) {
659 DCHECK(cached
.proof_valid());
661 if (!server_info_
|| !server_info_
->IsReadyToPersist()) {
665 QuicServerInfo::State
* state
= server_info_
->mutable_state();
667 state
->server_config
= cached
.server_config();
668 state
->source_address_token
= cached
.source_address_token();
669 state
->server_config_sig
= cached
.signature();
670 state
->certs
= cached
.certs();
672 server_info_
->Persist();
675 void QuicClientSession::OnProofVerifyDetailsAvailable(
676 const ProofVerifyDetails
& verify_details
) {
677 const ProofVerifyDetailsChromium
* verify_details_chromium
=
678 reinterpret_cast<const ProofVerifyDetailsChromium
*>(&verify_details
);
679 CertVerifyResult
* result_copy
= new CertVerifyResult
;
680 result_copy
->CopyFrom(verify_details_chromium
->cert_verify_result
);
681 cert_verify_result_
.reset(result_copy
);
682 pinning_failure_log_
= verify_details_chromium
->pinning_failure_log
;
683 logger_
->OnCertificateVerified(*cert_verify_result_
);
686 void QuicClientSession::StartReading() {
690 read_pending_
= true;
691 int rv
= socket_
->Read(read_buffer_
.get(),
692 read_buffer_
->size(),
693 base::Bind(&QuicClientSession::OnReadComplete
,
694 weak_factory_
.GetWeakPtr()));
695 if (rv
== ERR_IO_PENDING
) {
696 num_packets_read_
= 0;
700 if (++num_packets_read_
> 32) {
701 num_packets_read_
= 0;
702 // Data was read, process it.
703 // Schedule the work through the message loop to 1) prevent infinite
704 // recursion and 2) avoid blocking the thread for too long.
705 base::MessageLoop::current()->PostTask(
707 base::Bind(&QuicClientSession::OnReadComplete
,
708 weak_factory_
.GetWeakPtr(), rv
));
714 void QuicClientSession::CloseSessionOnError(int error
) {
715 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.CloseSessionOnError", -error
);
716 CloseSessionOnErrorInner(error
, QUIC_INTERNAL_ERROR
);
717 NotifyFactoryOfSessionClosed();
720 void QuicClientSession::CloseSessionOnErrorInner(int net_error
,
721 QuicErrorCode quic_error
) {
722 if (!callback_
.is_null()) {
723 base::ResetAndReturn(&callback_
).Run(net_error
);
725 CloseAllStreams(net_error
);
726 CloseAllObservers(net_error
);
728 NetLog::TYPE_QUIC_SESSION_CLOSE_ON_ERROR
,
729 NetLog::IntegerCallback("net_error", net_error
));
731 if (connection()->connected())
732 connection()->CloseConnection(quic_error
, false);
733 DCHECK(!connection()->connected());
736 void QuicClientSession::CloseAllStreams(int net_error
) {
737 while (!streams()->empty()) {
738 ReliableQuicStream
* stream
= streams()->begin()->second
;
739 QuicStreamId id
= stream
->id();
740 static_cast<QuicReliableClientStream
*>(stream
)->OnError(net_error
);
745 void QuicClientSession::CloseAllObservers(int net_error
) {
746 while (!observers_
.empty()) {
747 Observer
* observer
= *observers_
.begin();
748 observers_
.erase(observer
);
749 observer
->OnSessionClosed(net_error
);
753 base::Value
* QuicClientSession::GetInfoAsValue(
754 const std::set
<HostPortPair
>& aliases
) {
755 base::DictionaryValue
* dict
= new base::DictionaryValue();
756 dict
->SetString("version", QuicVersionToString(connection()->version()));
757 dict
->SetInteger("open_streams", GetNumOpenStreams());
758 base::ListValue
* stream_list
= new base::ListValue();
759 for (base::hash_map
<QuicStreamId
, QuicDataStream
*>::const_iterator it
760 = streams()->begin();
761 it
!= streams()->end();
763 stream_list
->Append(new base::StringValue(
764 base::Uint64ToString(it
->second
->id())));
766 dict
->Set("active_streams", stream_list
);
768 dict
->SetInteger("total_streams", num_total_streams_
);
769 dict
->SetString("peer_address", peer_address().ToString());
770 dict
->SetString("connection_id", base::Uint64ToString(connection_id()));
771 dict
->SetBoolean("connected", connection()->connected());
772 const QuicConnectionStats
& stats
= connection()->GetStats();
773 dict
->SetInteger("packets_sent", stats
.packets_sent
);
774 dict
->SetInteger("packets_received", stats
.packets_received
);
775 dict
->SetInteger("packets_lost", stats
.packets_lost
);
777 dict
->SetBoolean("secure", GetSSLInfo(&ssl_info
) && ssl_info
.cert
.get());
779 base::ListValue
* alias_list
= new base::ListValue();
780 for (std::set
<HostPortPair
>::const_iterator it
= aliases
.begin();
781 it
!= aliases
.end(); it
++) {
782 alias_list
->Append(new base::StringValue(it
->ToString()));
784 dict
->Set("aliases", alias_list
);
789 base::WeakPtr
<QuicClientSession
> QuicClientSession::GetWeakPtr() {
790 return weak_factory_
.GetWeakPtr();
793 void QuicClientSession::OnReadComplete(int result
) {
794 read_pending_
= false;
796 result
= ERR_CONNECTION_CLOSED
;
799 DVLOG(1) << "Closing session on read error: " << result
;
800 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.ReadError", -result
);
801 NotifyFactoryOfSessionGoingAway();
802 CloseSessionOnErrorInner(result
, QUIC_PACKET_READ_ERROR
);
803 NotifyFactoryOfSessionClosedLater();
807 QuicEncryptedPacket
packet(read_buffer_
->data(), result
);
808 IPEndPoint local_address
;
809 IPEndPoint peer_address
;
810 socket_
->GetLocalAddress(&local_address
);
811 socket_
->GetPeerAddress(&peer_address
);
812 // ProcessUdpPacket might result in |this| being deleted, so we
813 // use a weak pointer to be safe.
814 connection()->ProcessUdpPacket(local_address
, peer_address
, packet
);
815 if (!connection()->connected()) {
816 NotifyFactoryOfSessionClosedLater();
822 void QuicClientSession::NotifyFactoryOfSessionGoingAway() {
825 stream_factory_
->OnSessionGoingAway(this);
828 void QuicClientSession::NotifyFactoryOfSessionClosedLater() {
829 if (!streams()->empty())
830 RecordUnexpectedOpenStreams(NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER
);
833 RecordUnexpectedNotGoingAway(NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER
);
836 DCHECK_EQ(0u, GetNumOpenStreams());
837 DCHECK(!connection()->connected());
838 base::MessageLoop::current()->PostTask(
840 base::Bind(&QuicClientSession::NotifyFactoryOfSessionClosed
,
841 weak_factory_
.GetWeakPtr()));
844 void QuicClientSession::NotifyFactoryOfSessionClosed() {
845 if (!streams()->empty())
846 RecordUnexpectedOpenStreams(NOTIFY_FACTORY_OF_SESSION_CLOSED
);
849 RecordUnexpectedNotGoingAway(NOTIFY_FACTORY_OF_SESSION_CLOSED
);
852 DCHECK_EQ(0u, GetNumOpenStreams());
853 // Will delete |this|.
855 stream_factory_
->OnSessionClosed(this);
858 void QuicClientSession::OnConnectTimeout() {
859 DCHECK(callback_
.is_null());
860 DCHECK(IsEncryptionEstablished());
862 if (IsCryptoHandshakeConfirmed())
865 // TODO(rch): re-enable this code once beta is cut.
866 // if (stream_factory_)
867 // stream_factory_->OnSessionConnectTimeout(this);
868 // CloseAllStreams(ERR_QUIC_HANDSHAKE_FAILED);
869 // DCHECK_EQ(0u, GetNumOpenStreams());