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/quic/quic_connection_helper.h"
17 #include "net/quic/quic_crypto_client_stream_factory.h"
18 #include "net/quic/quic_default_packet_writer.h"
19 #include "net/quic/quic_stream_factory.h"
20 #include "net/ssl/ssl_info.h"
21 #include "net/udp/datagram_client_socket.h"
27 // Note: these values must be kept in sync with the corresponding values in:
28 // tools/metrics/histograms/histograms.xml
31 STATE_ENCRYPTION_ESTABLISHED
= 1,
32 STATE_HANDSHAKE_CONFIRMED
= 2,
34 NUM_HANDSHAKE_STATES
= 4
37 void RecordHandshakeState(HandshakeState state
) {
38 UMA_HISTOGRAM_ENUMERATION("Net.QuicHandshakeState", state
,
39 NUM_HANDSHAKE_STATES
);
44 QuicClientSession::StreamRequest::StreamRequest() : stream_(NULL
) {}
46 QuicClientSession::StreamRequest::~StreamRequest() {
50 int QuicClientSession::StreamRequest::StartRequest(
51 const base::WeakPtr
<QuicClientSession
>& session
,
52 QuicReliableClientStream
** stream
,
53 const CompletionCallback
& callback
) {
56 int rv
= session_
->TryCreateStream(this, stream_
);
57 if (rv
== ERR_IO_PENDING
) {
64 void QuicClientSession::StreamRequest::CancelRequest() {
66 session_
->CancelRequest(this);
71 void QuicClientSession::StreamRequest::OnRequestCompleteSuccess(
72 QuicReliableClientStream
* stream
) {
75 ResetAndReturn(&callback_
).Run(OK
);
78 void QuicClientSession::StreamRequest::OnRequestCompleteFailure(int rv
) {
80 ResetAndReturn(&callback_
).Run(rv
);
83 QuicClientSession::QuicClientSession(
84 QuicConnection
* connection
,
85 scoped_ptr
<DatagramClientSocket
> socket
,
86 scoped_ptr
<QuicDefaultPacketWriter
> writer
,
87 QuicStreamFactory
* stream_factory
,
88 QuicCryptoClientStreamFactory
* crypto_client_stream_factory
,
89 const string
& server_hostname
,
90 const QuicConfig
& config
,
91 QuicCryptoClientConfig
* crypto_config
,
93 : QuicSession(connection
, config
),
94 require_confirmation_(false),
95 stream_factory_(stream_factory
),
96 socket_(socket
.Pass()),
97 writer_(writer
.Pass()),
98 read_buffer_(new IOBufferWithSize(kMaxPacketSize
)),
100 num_total_streams_(0),
101 net_log_(BoundNetLog::Make(net_log
, NetLog::SOURCE_QUIC_SESSION
)),
103 num_packets_read_(0),
104 weak_factory_(this) {
105 crypto_stream_
.reset(
106 crypto_client_stream_factory
?
107 crypto_client_stream_factory
->CreateQuicCryptoClientStream(
108 server_hostname
, this, crypto_config
) :
109 new QuicCryptoClientStream(server_hostname
, this, crypto_config
));
111 connection
->set_debug_visitor(&logger_
);
112 // TODO(rch): pass in full host port proxy pair
114 NetLog::TYPE_QUIC_SESSION
,
115 NetLog::StringCallback("host", &server_hostname
));
118 QuicClientSession::~QuicClientSession() {
119 // The session must be closed before it is destroyed.
120 DCHECK(streams()->empty());
121 CloseAllStreams(ERR_UNEXPECTED
);
122 DCHECK(observers_
.empty());
123 CloseAllObservers(ERR_UNEXPECTED
);
125 connection()->set_debug_visitor(NULL
);
126 net_log_
.EndEvent(NetLog::TYPE_QUIC_SESSION
);
128 while (!stream_requests_
.empty()) {
129 StreamRequest
* request
= stream_requests_
.front();
130 stream_requests_
.pop_front();
131 request
->OnRequestCompleteFailure(ERR_ABORTED
);
134 if (IsEncryptionEstablished())
135 RecordHandshakeState(STATE_ENCRYPTION_ESTABLISHED
);
136 if (IsCryptoHandshakeConfirmed())
137 RecordHandshakeState(STATE_HANDSHAKE_CONFIRMED
);
139 RecordHandshakeState(STATE_FAILED
);
141 UMA_HISTOGRAM_COUNTS("Net.QuicSession.NumTotalStreams", num_total_streams_
);
142 UMA_HISTOGRAM_COUNTS("Net.QuicNumSentClientHellos",
143 crypto_stream_
->num_sent_client_hellos());
144 if (!IsCryptoHandshakeConfirmed())
147 // Sending one client_hello means we had zero handshake-round-trips.
148 int round_trip_handshakes
= crypto_stream_
->num_sent_client_hellos() - 1;
150 // TODO(jar): This histogram should be replaced by the HandshakeRoundTrips*
151 // histogram below. Until we get used to the new histogram, it is helpful to
152 // maintain the old one.
153 UMA_HISTOGRAM_COUNTS("Net.QuicNumSentClientHellosCryptoHandshakeConfirmed",
154 round_trip_handshakes
+ 1);
156 // Don't bother with these histogram during tests, which mock out
157 // num_sent_client_hellos().
158 if (round_trip_handshakes
< 0)
162 if (!crypto_stream_
->GetSSLInfo(&ssl_info
) || !ssl_info
.cert
) {
163 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.HandshakeRoundTripsForHTTP",
164 round_trip_handshakes
, 0, 3, 4);
166 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.HandshakeRoundTripsForHTTPS",
167 round_trip_handshakes
, 0, 3, 4);
171 bool QuicClientSession::OnStreamFrames(
172 const std::vector
<QuicStreamFrame
>& frames
) {
173 // Record total number of stream frames.
174 UMA_HISTOGRAM_COUNTS("Net.QuicNumStreamFramesInPacket", frames
.size());
176 // Record number of frames per stream in packet.
177 typedef std::map
<QuicStreamId
, size_t> FrameCounter
;
178 FrameCounter frames_per_stream
;
179 for (size_t i
= 0; i
< frames
.size(); ++i
) {
180 frames_per_stream
[frames
[i
].stream_id
]++;
182 for (FrameCounter::const_iterator it
= frames_per_stream
.begin();
183 it
!= frames_per_stream
.end(); ++it
) {
184 UMA_HISTOGRAM_COUNTS("Net.QuicNumStreamFramesPerStreamInPacket",
188 return QuicSession::OnStreamFrames(frames
);
191 void QuicClientSession::AddObserver(Observer
* observer
) {
192 DCHECK(!ContainsKey(observers_
, observer
));
193 observers_
.insert(observer
);
196 void QuicClientSession::RemoveObserver(Observer
* observer
) {
197 DCHECK(ContainsKey(observers_
, observer
));
198 observers_
.erase(observer
);
201 int QuicClientSession::TryCreateStream(StreamRequest
* request
,
202 QuicReliableClientStream
** stream
) {
203 if (!crypto_stream_
->encryption_established()) {
204 DLOG(DFATAL
) << "Encryption not established.";
205 return ERR_CONNECTION_CLOSED
;
208 if (goaway_received()) {
209 DVLOG(1) << "Going away.";
210 return ERR_CONNECTION_CLOSED
;
213 if (!connection()->connected()) {
214 DVLOG(1) << "Already closed.";
215 return ERR_CONNECTION_CLOSED
;
218 if (GetNumOpenStreams() < get_max_open_streams()) {
219 *stream
= CreateOutgoingReliableStreamImpl();
223 stream_requests_
.push_back(request
);
224 return ERR_IO_PENDING
;
227 void QuicClientSession::CancelRequest(StreamRequest
* request
) {
228 // Remove |request| from the queue while preserving the order of the
230 StreamRequestQueue::iterator it
=
231 std::find(stream_requests_
.begin(), stream_requests_
.end(), request
);
232 if (it
!= stream_requests_
.end()) {
233 it
= stream_requests_
.erase(it
);
237 QuicReliableClientStream
* QuicClientSession::CreateOutgoingDataStream() {
238 if (!crypto_stream_
->encryption_established()) {
239 DVLOG(1) << "Encryption not active so no outgoing stream created.";
242 if (GetNumOpenStreams() >= get_max_open_streams()) {
243 DVLOG(1) << "Failed to create a new outgoing stream. "
244 << "Already " << GetNumOpenStreams() << " open.";
247 if (goaway_received()) {
248 DVLOG(1) << "Failed to create a new outgoing stream. "
249 << "Already received goaway.";
253 return CreateOutgoingReliableStreamImpl();
256 QuicReliableClientStream
*
257 QuicClientSession::CreateOutgoingReliableStreamImpl() {
258 DCHECK(connection()->connected());
259 QuicReliableClientStream
* stream
=
260 new QuicReliableClientStream(GetNextStreamId(), this, net_log_
);
261 ActivateStream(stream
);
262 ++num_total_streams_
;
263 UMA_HISTOGRAM_COUNTS("Net.QuicSession.NumOpenStreams", GetNumOpenStreams());
267 QuicCryptoClientStream
* QuicClientSession::GetCryptoStream() {
268 return crypto_stream_
.get();
271 bool QuicClientSession::GetSSLInfo(SSLInfo
* ssl_info
) {
272 DCHECK(crypto_stream_
.get());
273 return crypto_stream_
->GetSSLInfo(ssl_info
);
276 int QuicClientSession::CryptoConnect(bool require_confirmation
,
277 const CompletionCallback
& callback
) {
278 require_confirmation_
= require_confirmation
;
279 RecordHandshakeState(STATE_STARTED
);
280 if (!crypto_stream_
->CryptoConnect()) {
281 // TODO(wtc): change crypto_stream_.CryptoConnect() to return a
282 // QuicErrorCode and map it to a net error code.
283 return ERR_CONNECTION_FAILED
;
286 bool can_notify
= require_confirmation_
?
287 IsCryptoHandshakeConfirmed() : IsEncryptionEstablished();
292 callback_
= callback
;
293 return ERR_IO_PENDING
;
296 int QuicClientSession::GetNumSentClientHellos() const {
297 return crypto_stream_
->num_sent_client_hellos();
300 bool QuicClientSession::CanPool(const std::string
& hostname
) const {
301 // TODO(rch): When QUIC supports channel ID or client certificates, this
302 // logic will need to be revised.
303 DCHECK(connection()->connected());
306 DCHECK(crypto_stream_
);
307 if (!crypto_stream_
->GetSSLInfo(&ssl_info
) || !ssl_info
.cert
) {
308 // We can always pool with insecure QUIC sessions.
311 // Only pool secure QUIC sessions if the cert matches the new hostname.
312 return ssl_info
.cert
->VerifyNameMatch(hostname
, &unused
);
315 QuicDataStream
* QuicClientSession::CreateIncomingDataStream(
317 DLOG(ERROR
) << "Server push not supported";
321 void QuicClientSession::CloseStream(QuicStreamId stream_id
) {
322 QuicSession::CloseStream(stream_id
);
326 void QuicClientSession::SendRstStream(QuicStreamId id
,
327 QuicRstStreamErrorCode error
) {
328 QuicSession::SendRstStream(id
, error
);
332 void QuicClientSession::OnClosedStream() {
333 if (GetNumOpenStreams() < get_max_open_streams() &&
334 !stream_requests_
.empty() &&
335 crypto_stream_
->encryption_established() &&
336 !goaway_received() &&
337 connection()->connected()) {
338 StreamRequest
* request
= stream_requests_
.front();
339 stream_requests_
.pop_front();
340 request
->OnRequestCompleteSuccess(CreateOutgoingReliableStreamImpl());
343 if (GetNumOpenStreams() == 0) {
344 stream_factory_
->OnIdleSession(this);
348 void QuicClientSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event
) {
349 if (!callback_
.is_null() &&
350 (!require_confirmation_
|| event
== HANDSHAKE_CONFIRMED
)) {
351 // TODO(rtenneti): Currently for all CryptoHandshakeEvent events, callback_
352 // could be called because there are no error events in CryptoHandshakeEvent
353 // enum. If error events are added to CryptoHandshakeEvent, then the
354 // following code needs to changed.
355 base::ResetAndReturn(&callback_
).Run(OK
);
357 if (event
== HANDSHAKE_CONFIRMED
) {
358 ObserverSet::iterator it
= observers_
.begin();
359 while (it
!= observers_
.end()) {
360 Observer
* observer
= *it
;
362 observer
->OnCryptoHandshakeConfirmed();
365 QuicSession::OnCryptoHandshakeEvent(event
);
368 void QuicClientSession::OnCryptoHandshakeMessageSent(
369 const CryptoHandshakeMessage
& message
) {
370 logger_
.OnCryptoHandshakeMessageSent(message
);
373 void QuicClientSession::OnCryptoHandshakeMessageReceived(
374 const CryptoHandshakeMessage
& message
) {
375 logger_
.OnCryptoHandshakeMessageReceived(message
);
378 void QuicClientSession::OnConnectionClosed(QuicErrorCode error
,
380 DCHECK(!connection()->connected());
381 logger_
.OnConnectionClosed(error
, from_peer
);
383 UMA_HISTOGRAM_SPARSE_SLOWLY(
384 "Net.QuicSession.ConnectionCloseErrorCodeServer", error
);
386 UMA_HISTOGRAM_SPARSE_SLOWLY(
387 "Net.QuicSession.ConnectionCloseErrorCodeClient", error
);
390 if (error
== QUIC_CONNECTION_TIMED_OUT
) {
391 UMA_HISTOGRAM_COUNTS(
392 "Net.QuicSession.ConnectionClose.NumOpenStreams.TimedOut",
393 GetNumOpenStreams());
394 if (!IsCryptoHandshakeConfirmed()) {
395 // If there have been any streams created, they were 0-RTT speculative
396 // requests that have not be serviced.
397 UMA_HISTOGRAM_COUNTS(
398 "Net.QuicSession.ConnectionClose.NumTotalStreams.HandshakeTimedOut",
403 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.QuicVersion",
404 connection()->version());
405 NotifyFactoryOfSessionGoingAway();
406 if (!callback_
.is_null()) {
407 base::ResetAndReturn(&callback_
).Run(ERR_QUIC_PROTOCOL_ERROR
);
410 QuicSession::OnConnectionClosed(error
, from_peer
);
411 DCHECK(streams()->empty());
412 CloseAllStreams(ERR_UNEXPECTED
);
413 CloseAllObservers(ERR_UNEXPECTED
);
414 NotifyFactoryOfSessionClosedLater();
417 void QuicClientSession::OnSuccessfulVersionNegotiation(
418 const QuicVersion
& version
) {
419 logger_
.OnSuccessfulVersionNegotiation(version
);
420 QuicSession::OnSuccessfulVersionNegotiation(version
);
423 void QuicClientSession::StartReading() {
427 read_pending_
= true;
428 int rv
= socket_
->Read(read_buffer_
.get(),
429 read_buffer_
->size(),
430 base::Bind(&QuicClientSession::OnReadComplete
,
431 weak_factory_
.GetWeakPtr()));
432 if (rv
== ERR_IO_PENDING
) {
433 num_packets_read_
= 0;
437 if (++num_packets_read_
> 32) {
438 num_packets_read_
= 0;
439 // Data was read, process it.
440 // Schedule the work through the message loop to avoid recursive
442 base::MessageLoop::current()->PostTask(
444 base::Bind(&QuicClientSession::OnReadComplete
,
445 weak_factory_
.GetWeakPtr(), rv
));
451 void QuicClientSession::CloseSessionOnError(int error
) {
452 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.CloseSessionOnError", -error
);
453 CloseSessionOnErrorInner(error
, QUIC_INTERNAL_ERROR
);
454 NotifyFactoryOfSessionClosed();
457 void QuicClientSession::CloseSessionOnErrorInner(int net_error
,
458 QuicErrorCode quic_error
) {
459 if (!callback_
.is_null()) {
460 base::ResetAndReturn(&callback_
).Run(net_error
);
462 CloseAllStreams(net_error
);
463 CloseAllObservers(net_error
);
465 NetLog::TYPE_QUIC_SESSION_CLOSE_ON_ERROR
,
466 NetLog::IntegerCallback("net_error", net_error
));
468 connection()->CloseConnection(quic_error
, false);
469 DCHECK(!connection()->connected());
472 void QuicClientSession::CloseAllStreams(int net_error
) {
473 while (!streams()->empty()) {
474 ReliableQuicStream
* stream
= streams()->begin()->second
;
475 QuicStreamId id
= stream
->id();
476 static_cast<QuicReliableClientStream
*>(stream
)->OnError(net_error
);
481 void QuicClientSession::CloseAllObservers(int net_error
) {
482 while (!observers_
.empty()) {
483 Observer
* observer
= *observers_
.begin();
484 observers_
.erase(observer
);
485 observer
->OnSessionClosed(net_error
);
489 base::Value
* QuicClientSession::GetInfoAsValue(
490 const std::set
<HostPortProxyPair
>& aliases
) const {
491 base::DictionaryValue
* dict
= new base::DictionaryValue();
492 // TODO(rch): remove "host_port_pair" when Chrome 34 is stable.
493 dict
->SetString("host_port_pair", aliases
.begin()->first
.ToString());
494 dict
->SetString("version", QuicVersionToString(connection()->version()));
495 dict
->SetInteger("open_streams", GetNumOpenStreams());
496 dict
->SetInteger("total_streams", num_total_streams_
);
497 dict
->SetString("peer_address", peer_address().ToString());
498 dict
->SetString("guid", base::Uint64ToString(guid()));
499 dict
->SetBoolean("connected", connection()->connected());
501 base::ListValue
* alias_list
= new base::ListValue();
502 for (std::set
<HostPortProxyPair
>::const_iterator it
= aliases
.begin();
503 it
!= aliases
.end(); it
++) {
504 alias_list
->Append(new base::StringValue(it
->first
.ToString()));
506 dict
->Set("aliases", alias_list
);
511 base::WeakPtr
<QuicClientSession
> QuicClientSession::GetWeakPtr() {
512 return weak_factory_
.GetWeakPtr();
515 void QuicClientSession::OnReadComplete(int result
) {
516 read_pending_
= false;
518 result
= ERR_CONNECTION_CLOSED
;
521 DVLOG(1) << "Closing session on read error: " << result
;
522 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.ReadError", -result
);
523 NotifyFactoryOfSessionGoingAway();
524 CloseSessionOnErrorInner(result
, QUIC_PACKET_READ_ERROR
);
525 NotifyFactoryOfSessionClosedLater();
529 scoped_refptr
<IOBufferWithSize
> buffer(read_buffer_
);
530 read_buffer_
= new IOBufferWithSize(kMaxPacketSize
);
531 QuicEncryptedPacket
packet(buffer
->data(), result
);
532 IPEndPoint local_address
;
533 IPEndPoint peer_address
;
534 socket_
->GetLocalAddress(&local_address
);
535 socket_
->GetPeerAddress(&peer_address
);
536 // ProcessUdpPacket might result in |this| being deleted, so we
537 // use a weak pointer to be safe.
538 connection()->ProcessUdpPacket(local_address
, peer_address
, packet
);
539 if (!connection()->connected()) {
540 NotifyFactoryOfSessionClosedLater();
546 void QuicClientSession::NotifyFactoryOfSessionGoingAway() {
548 stream_factory_
->OnSessionGoingAway(this);
551 void QuicClientSession::NotifyFactoryOfSessionClosedLater() {
552 DCHECK_EQ(0u, GetNumOpenStreams());
553 DCHECK(!connection()->connected());
554 base::MessageLoop::current()->PostTask(
556 base::Bind(&QuicClientSession::NotifyFactoryOfSessionClosed
,
557 weak_factory_
.GetWeakPtr()));
560 void QuicClientSession::NotifyFactoryOfSessionClosed() {
561 DCHECK_EQ(0u, GetNumOpenStreams());
562 // Will delete |this|.
564 stream_factory_
->OnSessionClosed(this);