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 // Don't bother with these histogram during tests, which mock out
151 // num_sent_client_hellos().
152 if (round_trip_handshakes < 0 || !stream_factory_)
155 bool port_selected = stream_factory_->enable_port_selection();
157 if (!crypto_stream_->GetSSLInfo(&ssl_info) || !ssl_info.cert) {
159 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectSelectPortForHTTP",
160 round_trip_handshakes, 0, 3, 4);
162 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectRandomPortForHTTP",
163 round_trip_handshakes, 0, 3, 4);
167 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectSelectPortForHTTPS",
168 round_trip_handshakes, 0, 3, 4);
170 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectRandomPortForHTTPS",
171 round_trip_handshakes, 0, 3, 4);
176 bool QuicClientSession::OnStreamFrames(
177 const std::vector<QuicStreamFrame>& frames) {
178 // Record total number of stream frames.
179 UMA_HISTOGRAM_COUNTS("Net.QuicNumStreamFramesInPacket", frames.size());
181 // Record number of frames per stream in packet.
182 typedef std::map<QuicStreamId, size_t> FrameCounter;
183 FrameCounter frames_per_stream;
184 for (size_t i = 0; i < frames.size(); ++i) {
185 frames_per_stream[frames[i].stream_id]++;
187 for (FrameCounter::const_iterator it = frames_per_stream.begin();
188 it != frames_per_stream.end(); ++it) {
189 UMA_HISTOGRAM_COUNTS("Net.QuicNumStreamFramesPerStreamInPacket",
193 return QuicSession::OnStreamFrames(frames);
196 void QuicClientSession::AddObserver(Observer* observer) {
197 DCHECK(!ContainsKey(observers_, observer));
198 observers_.insert(observer);
201 void QuicClientSession::RemoveObserver(Observer* observer) {
202 DCHECK(ContainsKey(observers_, observer));
203 observers_.erase(observer);
206 int QuicClientSession::TryCreateStream(StreamRequest* request,
207 QuicReliableClientStream** stream) {
208 if (!crypto_stream_->encryption_established()) {
209 DLOG(DFATAL) << "Encryption not established.";
210 return ERR_CONNECTION_CLOSED;
213 if (goaway_received()) {
214 DVLOG(1) << "Going away.";
215 return ERR_CONNECTION_CLOSED;
218 if (!connection()->connected()) {
219 DVLOG(1) << "Already closed.";
220 return ERR_CONNECTION_CLOSED;
223 if (GetNumOpenStreams() < get_max_open_streams()) {
224 *stream = CreateOutgoingReliableStreamImpl();
228 stream_requests_.push_back(request);
229 return ERR_IO_PENDING;
232 void QuicClientSession::CancelRequest(StreamRequest* request) {
233 // Remove |request| from the queue while preserving the order of the
235 StreamRequestQueue::iterator it =
236 std::find(stream_requests_.begin(), stream_requests_.end(), request);
237 if (it != stream_requests_.end()) {
238 it = stream_requests_.erase(it);
242 QuicReliableClientStream* QuicClientSession::CreateOutgoingDataStream() {
243 if (!crypto_stream_->encryption_established()) {
244 DVLOG(1) << "Encryption not active so no outgoing stream created.";
247 if (GetNumOpenStreams() >= get_max_open_streams()) {
248 DVLOG(1) << "Failed to create a new outgoing stream. "
249 << "Already " << GetNumOpenStreams() << " open.";
252 if (goaway_received()) {
253 DVLOG(1) << "Failed to create a new outgoing stream. "
254 << "Already received goaway.";
258 return CreateOutgoingReliableStreamImpl();
261 QuicReliableClientStream*
262 QuicClientSession::CreateOutgoingReliableStreamImpl() {
263 DCHECK(connection()->connected());
264 QuicReliableClientStream* stream =
265 new QuicReliableClientStream(GetNextStreamId(), this, net_log_);
266 ActivateStream(stream);
267 ++num_total_streams_;
268 UMA_HISTOGRAM_COUNTS("Net.QuicSession.NumOpenStreams", GetNumOpenStreams());
272 QuicCryptoClientStream* QuicClientSession::GetCryptoStream() {
273 return crypto_stream_.get();
276 bool QuicClientSession::GetSSLInfo(SSLInfo* ssl_info) {
277 DCHECK(crypto_stream_.get());
278 return crypto_stream_->GetSSLInfo(ssl_info);
281 int QuicClientSession::CryptoConnect(bool require_confirmation,
282 const CompletionCallback& callback) {
283 require_confirmation_ = require_confirmation;
284 RecordHandshakeState(STATE_STARTED);
285 if (!crypto_stream_->CryptoConnect()) {
286 // TODO(wtc): change crypto_stream_.CryptoConnect() to return a
287 // QuicErrorCode and map it to a net error code.
288 return ERR_CONNECTION_FAILED;
291 bool can_notify = require_confirmation_ ?
292 IsCryptoHandshakeConfirmed() : IsEncryptionEstablished();
297 callback_ = callback;
298 return ERR_IO_PENDING;
301 int QuicClientSession::GetNumSentClientHellos() const {
302 return crypto_stream_->num_sent_client_hellos();
305 bool QuicClientSession::CanPool(const std::string& hostname) const {
306 // TODO(rch): When QUIC supports channel ID or client certificates, this
307 // logic will need to be revised.
308 DCHECK(connection()->connected());
311 DCHECK(crypto_stream_);
312 if (!crypto_stream_->GetSSLInfo(&ssl_info) || !ssl_info.cert) {
313 // We can always pool with insecure QUIC sessions.
316 // Only pool secure QUIC sessions if the cert matches the new hostname.
317 return ssl_info.cert->VerifyNameMatch(hostname, &unused);
320 QuicDataStream* QuicClientSession::CreateIncomingDataStream(
322 DLOG(ERROR) << "Server push not supported";
326 void QuicClientSession::CloseStream(QuicStreamId stream_id) {
327 QuicSession::CloseStream(stream_id);
331 void QuicClientSession::SendRstStream(QuicStreamId id,
332 QuicRstStreamErrorCode error,
333 QuicStreamOffset bytes_written) {
334 QuicSession::SendRstStream(id, error, bytes_written);
338 void QuicClientSession::OnClosedStream() {
339 if (GetNumOpenStreams() < get_max_open_streams() &&
340 !stream_requests_.empty() &&
341 crypto_stream_->encryption_established() &&
342 !goaway_received() &&
343 connection()->connected()) {
344 StreamRequest* request = stream_requests_.front();
345 stream_requests_.pop_front();
346 request->OnRequestCompleteSuccess(CreateOutgoingReliableStreamImpl());
349 if (GetNumOpenStreams() == 0) {
350 stream_factory_->OnIdleSession(this);
354 void QuicClientSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
355 if (!callback_.is_null() &&
356 (!require_confirmation_ || event == HANDSHAKE_CONFIRMED)) {
357 // TODO(rtenneti): Currently for all CryptoHandshakeEvent events, callback_
358 // could be called because there are no error events in CryptoHandshakeEvent
359 // enum. If error events are added to CryptoHandshakeEvent, then the
360 // following code needs to changed.
361 base::ResetAndReturn(&callback_).Run(OK);
363 if (event == HANDSHAKE_CONFIRMED) {
364 ObserverSet::iterator it = observers_.begin();
365 while (it != observers_.end()) {
366 Observer* observer = *it;
368 observer->OnCryptoHandshakeConfirmed();
371 QuicSession::OnCryptoHandshakeEvent(event);
374 void QuicClientSession::OnCryptoHandshakeMessageSent(
375 const CryptoHandshakeMessage& message) {
376 logger_.OnCryptoHandshakeMessageSent(message);
379 void QuicClientSession::OnCryptoHandshakeMessageReceived(
380 const CryptoHandshakeMessage& message) {
381 logger_.OnCryptoHandshakeMessageReceived(message);
384 void QuicClientSession::OnConnectionClosed(QuicErrorCode error,
386 DCHECK(!connection()->connected());
387 logger_.OnConnectionClosed(error, from_peer);
389 UMA_HISTOGRAM_SPARSE_SLOWLY(
390 "Net.QuicSession.ConnectionCloseErrorCodeServer", error);
392 UMA_HISTOGRAM_SPARSE_SLOWLY(
393 "Net.QuicSession.ConnectionCloseErrorCodeClient", error);
396 if (error == QUIC_CONNECTION_TIMED_OUT) {
397 UMA_HISTOGRAM_COUNTS(
398 "Net.QuicSession.ConnectionClose.NumOpenStreams.TimedOut",
399 GetNumOpenStreams());
400 if (!IsCryptoHandshakeConfirmed()) {
401 // If there have been any streams created, they were 0-RTT speculative
402 // requests that have not be serviced.
403 UMA_HISTOGRAM_COUNTS(
404 "Net.QuicSession.ConnectionClose.NumTotalStreams.HandshakeTimedOut",
409 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.QuicVersion",
410 connection()->version());
411 NotifyFactoryOfSessionGoingAway();
412 if (!callback_.is_null()) {
413 base::ResetAndReturn(&callback_).Run(ERR_QUIC_PROTOCOL_ERROR);
416 QuicSession::OnConnectionClosed(error, from_peer);
417 DCHECK(streams()->empty());
418 CloseAllStreams(ERR_UNEXPECTED);
419 CloseAllObservers(ERR_UNEXPECTED);
420 NotifyFactoryOfSessionClosedLater();
423 void QuicClientSession::OnSuccessfulVersionNegotiation(
424 const QuicVersion& version) {
425 logger_.OnSuccessfulVersionNegotiation(version);
426 QuicSession::OnSuccessfulVersionNegotiation(version);
429 void QuicClientSession::StartReading() {
433 read_pending_ = true;
434 int rv = socket_->Read(read_buffer_.get(),
435 read_buffer_->size(),
436 base::Bind(&QuicClientSession::OnReadComplete,
437 weak_factory_.GetWeakPtr()));
438 if (rv == ERR_IO_PENDING) {
439 num_packets_read_ = 0;
443 if (++num_packets_read_ > 32) {
444 num_packets_read_ = 0;
445 // Data was read, process it.
446 // Schedule the work through the message loop to avoid recursive
448 base::MessageLoop::current()->PostTask(
450 base::Bind(&QuicClientSession::OnReadComplete,
451 weak_factory_.GetWeakPtr(), rv));
457 void QuicClientSession::CloseSessionOnError(int error) {
458 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.CloseSessionOnError", -error);
459 CloseSessionOnErrorInner(error, QUIC_INTERNAL_ERROR);
460 NotifyFactoryOfSessionClosed();
463 void QuicClientSession::CloseSessionOnErrorInner(int net_error,
464 QuicErrorCode quic_error) {
465 if (!callback_.is_null()) {
466 base::ResetAndReturn(&callback_).Run(net_error);
468 CloseAllStreams(net_error);
469 CloseAllObservers(net_error);
471 NetLog::TYPE_QUIC_SESSION_CLOSE_ON_ERROR,
472 NetLog::IntegerCallback("net_error", net_error));
474 connection()->CloseConnection(quic_error, false);
475 DCHECK(!connection()->connected());
478 void QuicClientSession::CloseAllStreams(int net_error) {
479 while (!streams()->empty()) {
480 ReliableQuicStream* stream = streams()->begin()->second;
481 QuicStreamId id = stream->id();
482 static_cast<QuicReliableClientStream*>(stream)->OnError(net_error);
487 void QuicClientSession::CloseAllObservers(int net_error) {
488 while (!observers_.empty()) {
489 Observer* observer = *observers_.begin();
490 observers_.erase(observer);
491 observer->OnSessionClosed(net_error);
495 base::Value* QuicClientSession::GetInfoAsValue(
496 const std::set<HostPortProxyPair>& aliases) const {
497 base::DictionaryValue* dict = new base::DictionaryValue();
498 // TODO(rch): remove "host_port_pair" when Chrome 34 is stable.
499 dict->SetString("host_port_pair", aliases.begin()->first.ToString());
500 dict->SetString("version", QuicVersionToString(connection()->version()));
501 dict->SetInteger("open_streams", GetNumOpenStreams());
502 dict->SetInteger("total_streams", num_total_streams_);
503 dict->SetString("peer_address", peer_address().ToString());
504 dict->SetString("guid", base::Uint64ToString(guid()));
505 dict->SetBoolean("connected", connection()->connected());
507 base::ListValue* alias_list = new base::ListValue();
508 for (std::set<HostPortProxyPair>::const_iterator it = aliases.begin();
509 it != aliases.end(); it++) {
510 alias_list->Append(new base::StringValue(it->first.ToString()));
512 dict->Set("aliases", alias_list);
517 base::WeakPtr<QuicClientSession> QuicClientSession::GetWeakPtr() {
518 return weak_factory_.GetWeakPtr();
521 void QuicClientSession::OnReadComplete(int result) {
522 read_pending_ = false;
524 result = ERR_CONNECTION_CLOSED;
527 DVLOG(1) << "Closing session on read error: " << result;
528 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.ReadError", -result);
529 NotifyFactoryOfSessionGoingAway();
530 CloseSessionOnErrorInner(result, QUIC_PACKET_READ_ERROR);
531 NotifyFactoryOfSessionClosedLater();
535 scoped_refptr<IOBufferWithSize> buffer(read_buffer_);
536 read_buffer_ = new IOBufferWithSize(kMaxPacketSize);
537 QuicEncryptedPacket packet(buffer->data(), result);
538 IPEndPoint local_address;
539 IPEndPoint peer_address;
540 socket_->GetLocalAddress(&local_address);
541 socket_->GetPeerAddress(&peer_address);
542 // ProcessUdpPacket might result in |this| being deleted, so we
543 // use a weak pointer to be safe.
544 connection()->ProcessUdpPacket(local_address, peer_address, packet);
545 if (!connection()->connected()) {
546 NotifyFactoryOfSessionClosedLater();
552 void QuicClientSession::NotifyFactoryOfSessionGoingAway() {
554 stream_factory_->OnSessionGoingAway(this);
557 void QuicClientSession::NotifyFactoryOfSessionClosedLater() {
558 DCHECK_EQ(0u, GetNumOpenStreams());
559 DCHECK(!connection()->connected());
560 base::MessageLoop::current()->PostTask(
562 base::Bind(&QuicClientSession::NotifyFactoryOfSessionClosed,
563 weak_factory_.GetWeakPtr()));
566 void QuicClientSession::NotifyFactoryOfSessionClosed() {
567 DCHECK_EQ(0u, GetNumOpenStreams());
568 // Will delete |this|.
570 stream_factory_->OnSessionClosed(this);