| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #include "net/quic/quic_client_session.h" | |
| 6 | |
| 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/profiler/scoped_tracker.h" | |
| 12 #include "base/stl_util.h" | |
| 13 #include "base/strings/string_number_conversions.h" | |
| 14 #include "base/values.h" | |
| 15 #include "net/base/io_buffer.h" | |
| 16 #include "net/base/net_errors.h" | |
| 17 #include "net/base/network_activity_monitor.h" | |
| 18 #include "net/http/transport_security_state.h" | |
| 19 #include "net/quic/crypto/proof_verifier_chromium.h" | |
| 20 #include "net/quic/crypto/quic_server_info.h" | |
| 21 #include "net/quic/quic_connection_helper.h" | |
| 22 #include "net/quic/quic_crypto_client_stream_factory.h" | |
| 23 #include "net/quic/quic_server_id.h" | |
| 24 #include "net/quic/quic_stream_factory.h" | |
| 25 #include "net/spdy/spdy_session.h" | |
| 26 #include "net/ssl/channel_id_service.h" | |
| 27 #include "net/ssl/ssl_connection_status_flags.h" | |
| 28 #include "net/ssl/ssl_info.h" | |
| 29 #include "net/udp/datagram_client_socket.h" | |
| 30 | |
| 31 namespace net { | |
| 32 | |
| 33 namespace { | |
| 34 | |
| 35 // The length of time to wait for a 0-RTT handshake to complete | |
| 36 // before allowing the requests to possibly proceed over TCP. | |
| 37 const int k0RttHandshakeTimeoutMs = 300; | |
| 38 | |
| 39 // IPv6 packets have an additional 20 bytes of overhead than IPv4 packets. | |
| 40 const size_t kAdditionalOverheadForIPv6 = 20; | |
| 41 | |
| 42 // Histograms for tracking down the crashes from http://crbug.com/354669 | |
| 43 // Note: these values must be kept in sync with the corresponding values in: | |
| 44 // tools/metrics/histograms/histograms.xml | |
| 45 enum Location { | |
| 46 DESTRUCTOR = 0, | |
| 47 ADD_OBSERVER = 1, | |
| 48 TRY_CREATE_STREAM = 2, | |
| 49 CREATE_OUTGOING_RELIABLE_STREAM = 3, | |
| 50 NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER = 4, | |
| 51 NOTIFY_FACTORY_OF_SESSION_CLOSED = 5, | |
| 52 NUM_LOCATIONS = 6, | |
| 53 }; | |
| 54 | |
| 55 void RecordUnexpectedOpenStreams(Location location) { | |
| 56 UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedOpenStreams", location, | |
| 57 NUM_LOCATIONS); | |
| 58 } | |
| 59 | |
| 60 void RecordUnexpectedObservers(Location location) { | |
| 61 UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedObservers", location, | |
| 62 NUM_LOCATIONS); | |
| 63 } | |
| 64 | |
| 65 void RecordUnexpectedNotGoingAway(Location location) { | |
| 66 UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedNotGoingAway", location, | |
| 67 NUM_LOCATIONS); | |
| 68 } | |
| 69 | |
| 70 // Histogram for recording the different reasons that a QUIC session is unable | |
| 71 // to complete the handshake. | |
| 72 enum HandshakeFailureReason { | |
| 73 HANDSHAKE_FAILURE_UNKNOWN = 0, | |
| 74 HANDSHAKE_FAILURE_BLACK_HOLE = 1, | |
| 75 HANDSHAKE_FAILURE_PUBLIC_RESET = 2, | |
| 76 NUM_HANDSHAKE_FAILURE_REASONS = 3, | |
| 77 }; | |
| 78 | |
| 79 void RecordHandshakeFailureReason(HandshakeFailureReason reason) { | |
| 80 UMA_HISTOGRAM_ENUMERATION( | |
| 81 "Net.QuicSession.ConnectionClose.HandshakeNotConfirmed.Reason", | |
| 82 reason, NUM_HANDSHAKE_FAILURE_REASONS); | |
| 83 } | |
| 84 | |
| 85 // Note: these values must be kept in sync with the corresponding values in: | |
| 86 // tools/metrics/histograms/histograms.xml | |
| 87 enum HandshakeState { | |
| 88 STATE_STARTED = 0, | |
| 89 STATE_ENCRYPTION_ESTABLISHED = 1, | |
| 90 STATE_HANDSHAKE_CONFIRMED = 2, | |
| 91 STATE_FAILED = 3, | |
| 92 NUM_HANDSHAKE_STATES = 4 | |
| 93 }; | |
| 94 | |
| 95 void RecordHandshakeState(HandshakeState state) { | |
| 96 UMA_HISTOGRAM_ENUMERATION("Net.QuicHandshakeState", state, | |
| 97 NUM_HANDSHAKE_STATES); | |
| 98 } | |
| 99 | |
| 100 base::Value* NetLogQuicClientSessionCallback( | |
| 101 const QuicServerId* server_id, | |
| 102 bool require_confirmation, | |
| 103 NetLog::LogLevel /* log_level */) { | |
| 104 base::DictionaryValue* dict = new base::DictionaryValue(); | |
| 105 dict->SetString("host", server_id->host()); | |
| 106 dict->SetInteger("port", server_id->port()); | |
| 107 dict->SetBoolean("is_https", server_id->is_https()); | |
| 108 dict->SetBoolean("privacy_mode", | |
| 109 server_id->privacy_mode() == PRIVACY_MODE_ENABLED); | |
| 110 dict->SetBoolean("require_confirmation", require_confirmation); | |
| 111 return dict; | |
| 112 } | |
| 113 | |
| 114 } // namespace | |
| 115 | |
| 116 QuicClientSession::StreamRequest::StreamRequest() : stream_(nullptr) {} | |
| 117 | |
| 118 QuicClientSession::StreamRequest::~StreamRequest() { | |
| 119 CancelRequest(); | |
| 120 } | |
| 121 | |
| 122 int QuicClientSession::StreamRequest::StartRequest( | |
| 123 const base::WeakPtr<QuicClientSession>& session, | |
| 124 QuicReliableClientStream** stream, | |
| 125 const CompletionCallback& callback) { | |
| 126 session_ = session; | |
| 127 stream_ = stream; | |
| 128 int rv = session_->TryCreateStream(this, stream_); | |
| 129 if (rv == ERR_IO_PENDING) { | |
| 130 callback_ = callback; | |
| 131 } | |
| 132 | |
| 133 return rv; | |
| 134 } | |
| 135 | |
| 136 void QuicClientSession::StreamRequest::CancelRequest() { | |
| 137 if (session_) | |
| 138 session_->CancelRequest(this); | |
| 139 session_.reset(); | |
| 140 callback_.Reset(); | |
| 141 } | |
| 142 | |
| 143 void QuicClientSession::StreamRequest::OnRequestCompleteSuccess( | |
| 144 QuicReliableClientStream* stream) { | |
| 145 session_.reset(); | |
| 146 *stream_ = stream; | |
| 147 ResetAndReturn(&callback_).Run(OK); | |
| 148 } | |
| 149 | |
| 150 void QuicClientSession::StreamRequest::OnRequestCompleteFailure(int rv) { | |
| 151 session_.reset(); | |
| 152 ResetAndReturn(&callback_).Run(rv); | |
| 153 } | |
| 154 | |
| 155 QuicClientSession::QuicClientSession( | |
| 156 QuicConnection* connection, | |
| 157 scoped_ptr<DatagramClientSocket> socket, | |
| 158 QuicStreamFactory* stream_factory, | |
| 159 TransportSecurityState* transport_security_state, | |
| 160 scoped_ptr<QuicServerInfo> server_info, | |
| 161 const QuicConfig& config, | |
| 162 base::TaskRunner* task_runner, | |
| 163 NetLog* net_log) | |
| 164 : QuicClientSessionBase(connection, config), | |
| 165 require_confirmation_(false), | |
| 166 stream_factory_(stream_factory), | |
| 167 socket_(socket.Pass()), | |
| 168 read_buffer_(new IOBufferWithSize(kMaxPacketSize)), | |
| 169 transport_security_state_(transport_security_state), | |
| 170 server_info_(server_info.Pass()), | |
| 171 read_pending_(false), | |
| 172 num_total_streams_(0), | |
| 173 task_runner_(task_runner), | |
| 174 net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_QUIC_SESSION)), | |
| 175 logger_(new QuicConnectionLogger(this, net_log_)), | |
| 176 num_packets_read_(0), | |
| 177 going_away_(false), | |
| 178 weak_factory_(this) { | |
| 179 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 180 tracked_objects::ScopedTracker tracking_profile1( | |
| 181 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 182 "422516 QuicClientSession::QuicClientSession1")); | |
| 183 | |
| 184 connection->set_debug_visitor(logger_); | |
| 185 IPEndPoint address; | |
| 186 // TODO(rtenneti): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 187 tracked_objects::ScopedTracker tracking_profile2( | |
| 188 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 189 "422516 QuicClientSession::QuicClientSession2")); | |
| 190 if (socket && socket->GetLocalAddress(&address) == OK && | |
| 191 address.GetFamily() == ADDRESS_FAMILY_IPV6) { | |
| 192 connection->set_max_packet_length( | |
| 193 connection->max_packet_length() - kAdditionalOverheadForIPv6); | |
| 194 } | |
| 195 } | |
| 196 | |
| 197 void QuicClientSession::InitializeSession( | |
| 198 const QuicServerId& server_id, | |
| 199 QuicCryptoClientConfig* crypto_config, | |
| 200 QuicCryptoClientStreamFactory* crypto_client_stream_factory) { | |
| 201 server_id_ = server_id; | |
| 202 crypto_stream_.reset( | |
| 203 crypto_client_stream_factory ? | |
| 204 crypto_client_stream_factory->CreateQuicCryptoClientStream( | |
| 205 server_id, this, crypto_config) : | |
| 206 new QuicCryptoClientStream(server_id, this, | |
| 207 new ProofVerifyContextChromium(net_log_), | |
| 208 crypto_config)); | |
| 209 QuicClientSessionBase::InitializeSession(); | |
| 210 // TODO(rch): pass in full host port proxy pair | |
| 211 net_log_.BeginEvent(NetLog::TYPE_QUIC_SESSION, | |
| 212 base::Bind(NetLogQuicClientSessionCallback, | |
| 213 &server_id, | |
| 214 require_confirmation_)); | |
| 215 } | |
| 216 | |
| 217 QuicClientSession::~QuicClientSession() { | |
| 218 if (!streams()->empty()) | |
| 219 RecordUnexpectedOpenStreams(DESTRUCTOR); | |
| 220 if (!observers_.empty()) | |
| 221 RecordUnexpectedObservers(DESTRUCTOR); | |
| 222 if (!going_away_) | |
| 223 RecordUnexpectedNotGoingAway(DESTRUCTOR); | |
| 224 | |
| 225 while (!streams()->empty() || | |
| 226 !observers_.empty() || | |
| 227 !stream_requests_.empty()) { | |
| 228 // The session must be closed before it is destroyed. | |
| 229 DCHECK(streams()->empty()); | |
| 230 CloseAllStreams(ERR_UNEXPECTED); | |
| 231 DCHECK(observers_.empty()); | |
| 232 CloseAllObservers(ERR_UNEXPECTED); | |
| 233 | |
| 234 connection()->set_debug_visitor(nullptr); | |
| 235 net_log_.EndEvent(NetLog::TYPE_QUIC_SESSION); | |
| 236 | |
| 237 while (!stream_requests_.empty()) { | |
| 238 StreamRequest* request = stream_requests_.front(); | |
| 239 stream_requests_.pop_front(); | |
| 240 request->OnRequestCompleteFailure(ERR_ABORTED); | |
| 241 } | |
| 242 } | |
| 243 | |
| 244 if (connection()->connected()) { | |
| 245 // Ensure that the connection is closed by the time the session is | |
| 246 // destroyed. | |
| 247 connection()->CloseConnection(QUIC_INTERNAL_ERROR, false); | |
| 248 } | |
| 249 | |
| 250 if (IsEncryptionEstablished()) | |
| 251 RecordHandshakeState(STATE_ENCRYPTION_ESTABLISHED); | |
| 252 if (IsCryptoHandshakeConfirmed()) | |
| 253 RecordHandshakeState(STATE_HANDSHAKE_CONFIRMED); | |
| 254 else | |
| 255 RecordHandshakeState(STATE_FAILED); | |
| 256 | |
| 257 UMA_HISTOGRAM_COUNTS("Net.QuicSession.NumTotalStreams", num_total_streams_); | |
| 258 UMA_HISTOGRAM_COUNTS("Net.QuicNumSentClientHellos", | |
| 259 crypto_stream_->num_sent_client_hellos()); | |
| 260 if (!IsCryptoHandshakeConfirmed()) | |
| 261 return; | |
| 262 | |
| 263 // Sending one client_hello means we had zero handshake-round-trips. | |
| 264 int round_trip_handshakes = crypto_stream_->num_sent_client_hellos() - 1; | |
| 265 | |
| 266 // Don't bother with these histogram during tests, which mock out | |
| 267 // num_sent_client_hellos(). | |
| 268 if (round_trip_handshakes < 0 || !stream_factory_) | |
| 269 return; | |
| 270 | |
| 271 bool port_selected = stream_factory_->enable_port_selection(); | |
| 272 SSLInfo ssl_info; | |
| 273 if (!GetSSLInfo(&ssl_info) || !ssl_info.cert.get()) { | |
| 274 if (port_selected) { | |
| 275 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectSelectPortForHTTP", | |
| 276 round_trip_handshakes, 0, 3, 4); | |
| 277 } else { | |
| 278 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectRandomPortForHTTP", | |
| 279 round_trip_handshakes, 0, 3, 4); | |
| 280 if (require_confirmation_) { | |
| 281 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
| 282 "Net.QuicSession.ConnectRandomPortRequiringConfirmationForHTTP", | |
| 283 round_trip_handshakes, 0, 3, 4); | |
| 284 } | |
| 285 } | |
| 286 } else { | |
| 287 if (port_selected) { | |
| 288 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectSelectPortForHTTPS", | |
| 289 round_trip_handshakes, 0, 3, 4); | |
| 290 } else { | |
| 291 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectRandomPortForHTTPS", | |
| 292 round_trip_handshakes, 0, 3, 4); | |
| 293 if (require_confirmation_) { | |
| 294 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
| 295 "Net.QuicSession.ConnectRandomPortRequiringConfirmationForHTTPS", | |
| 296 round_trip_handshakes, 0, 3, 4); | |
| 297 } | |
| 298 } | |
| 299 } | |
| 300 const QuicConnectionStats stats = connection()->GetStats(); | |
| 301 if (server_info_ && stats.min_rtt_us > 0) { | |
| 302 base::TimeTicks wait_for_data_start_time = | |
| 303 server_info_->wait_for_data_start_time(); | |
| 304 base::TimeTicks wait_for_data_end_time = | |
| 305 server_info_->wait_for_data_end_time(); | |
| 306 if (!wait_for_data_start_time.is_null() && | |
| 307 !wait_for_data_end_time.is_null()) { | |
| 308 base::TimeDelta wait_time = | |
| 309 wait_for_data_end_time - wait_for_data_start_time; | |
| 310 const base::HistogramBase::Sample kMaxWaitToRtt = 1000; | |
| 311 base::HistogramBase::Sample wait_to_rtt = | |
| 312 static_cast<base::HistogramBase::Sample>( | |
| 313 100 * wait_time.InMicroseconds() / stats.min_rtt_us); | |
| 314 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicServerInfo.WaitForDataReadyToRtt", | |
| 315 wait_to_rtt, 0, kMaxWaitToRtt, 50); | |
| 316 } | |
| 317 } | |
| 318 | |
| 319 if (stats.max_sequence_reordering == 0) | |
| 320 return; | |
| 321 const base::HistogramBase::Sample kMaxReordering = 100; | |
| 322 base::HistogramBase::Sample reordering = kMaxReordering; | |
| 323 if (stats.min_rtt_us > 0) { | |
| 324 reordering = static_cast<base::HistogramBase::Sample>( | |
| 325 100 * stats.max_time_reordering_us / stats.min_rtt_us); | |
| 326 } | |
| 327 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.MaxReorderingTime", | |
| 328 reordering, 0, kMaxReordering, 50); | |
| 329 if (stats.min_rtt_us > 100 * 1000) { | |
| 330 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.MaxReorderingTimeLongRtt", | |
| 331 reordering, 0, kMaxReordering, 50); | |
| 332 } | |
| 333 UMA_HISTOGRAM_COUNTS( | |
| 334 "Net.QuicSession.MaxReordering", | |
| 335 static_cast<base::HistogramBase::Sample>(stats.max_sequence_reordering)); | |
| 336 } | |
| 337 | |
| 338 void QuicClientSession::OnStreamFrames( | |
| 339 const std::vector<QuicStreamFrame>& frames) { | |
| 340 // Record total number of stream frames. | |
| 341 UMA_HISTOGRAM_COUNTS("Net.QuicNumStreamFramesInPacket", frames.size()); | |
| 342 | |
| 343 // Record number of frames per stream in packet. | |
| 344 typedef std::map<QuicStreamId, size_t> FrameCounter; | |
| 345 FrameCounter frames_per_stream; | |
| 346 for (size_t i = 0; i < frames.size(); ++i) { | |
| 347 frames_per_stream[frames[i].stream_id]++; | |
| 348 } | |
| 349 for (FrameCounter::const_iterator it = frames_per_stream.begin(); | |
| 350 it != frames_per_stream.end(); ++it) { | |
| 351 UMA_HISTOGRAM_COUNTS("Net.QuicNumStreamFramesPerStreamInPacket", | |
| 352 it->second); | |
| 353 } | |
| 354 | |
| 355 return QuicSession::OnStreamFrames(frames); | |
| 356 } | |
| 357 | |
| 358 void QuicClientSession::AddObserver(Observer* observer) { | |
| 359 if (going_away_) { | |
| 360 RecordUnexpectedObservers(ADD_OBSERVER); | |
| 361 observer->OnSessionClosed(ERR_UNEXPECTED); | |
| 362 return; | |
| 363 } | |
| 364 | |
| 365 DCHECK(!ContainsKey(observers_, observer)); | |
| 366 observers_.insert(observer); | |
| 367 } | |
| 368 | |
| 369 void QuicClientSession::RemoveObserver(Observer* observer) { | |
| 370 DCHECK(ContainsKey(observers_, observer)); | |
| 371 observers_.erase(observer); | |
| 372 } | |
| 373 | |
| 374 int QuicClientSession::TryCreateStream(StreamRequest* request, | |
| 375 QuicReliableClientStream** stream) { | |
| 376 if (!crypto_stream_->encryption_established()) { | |
| 377 DLOG(DFATAL) << "Encryption not established."; | |
| 378 return ERR_CONNECTION_CLOSED; | |
| 379 } | |
| 380 | |
| 381 if (goaway_received()) { | |
| 382 DVLOG(1) << "Going away."; | |
| 383 return ERR_CONNECTION_CLOSED; | |
| 384 } | |
| 385 | |
| 386 if (!connection()->connected()) { | |
| 387 DVLOG(1) << "Already closed."; | |
| 388 return ERR_CONNECTION_CLOSED; | |
| 389 } | |
| 390 | |
| 391 if (going_away_) { | |
| 392 RecordUnexpectedOpenStreams(TRY_CREATE_STREAM); | |
| 393 return ERR_CONNECTION_CLOSED; | |
| 394 } | |
| 395 | |
| 396 if (GetNumOpenStreams() < get_max_open_streams()) { | |
| 397 *stream = CreateOutgoingReliableStreamImpl(); | |
| 398 return OK; | |
| 399 } | |
| 400 | |
| 401 stream_requests_.push_back(request); | |
| 402 return ERR_IO_PENDING; | |
| 403 } | |
| 404 | |
| 405 void QuicClientSession::CancelRequest(StreamRequest* request) { | |
| 406 // Remove |request| from the queue while preserving the order of the | |
| 407 // other elements. | |
| 408 StreamRequestQueue::iterator it = | |
| 409 std::find(stream_requests_.begin(), stream_requests_.end(), request); | |
| 410 if (it != stream_requests_.end()) { | |
| 411 it = stream_requests_.erase(it); | |
| 412 } | |
| 413 } | |
| 414 | |
| 415 QuicReliableClientStream* QuicClientSession::CreateOutgoingDataStream() { | |
| 416 if (!crypto_stream_->encryption_established()) { | |
| 417 DVLOG(1) << "Encryption not active so no outgoing stream created."; | |
| 418 return nullptr; | |
| 419 } | |
| 420 if (GetNumOpenStreams() >= get_max_open_streams()) { | |
| 421 DVLOG(1) << "Failed to create a new outgoing stream. " | |
| 422 << "Already " << GetNumOpenStreams() << " open."; | |
| 423 return nullptr; | |
| 424 } | |
| 425 if (goaway_received()) { | |
| 426 DVLOG(1) << "Failed to create a new outgoing stream. " | |
| 427 << "Already received goaway."; | |
| 428 return nullptr; | |
| 429 } | |
| 430 if (going_away_) { | |
| 431 RecordUnexpectedOpenStreams(CREATE_OUTGOING_RELIABLE_STREAM); | |
| 432 return nullptr; | |
| 433 } | |
| 434 return CreateOutgoingReliableStreamImpl(); | |
| 435 } | |
| 436 | |
| 437 QuicReliableClientStream* | |
| 438 QuicClientSession::CreateOutgoingReliableStreamImpl() { | |
| 439 DCHECK(connection()->connected()); | |
| 440 QuicReliableClientStream* stream = | |
| 441 new QuicReliableClientStream(GetNextStreamId(), this, net_log_); | |
| 442 ActivateStream(stream); | |
| 443 ++num_total_streams_; | |
| 444 UMA_HISTOGRAM_COUNTS("Net.QuicSession.NumOpenStreams", GetNumOpenStreams()); | |
| 445 return stream; | |
| 446 } | |
| 447 | |
| 448 QuicCryptoClientStream* QuicClientSession::GetCryptoStream() { | |
| 449 return crypto_stream_.get(); | |
| 450 }; | |
| 451 | |
| 452 // TODO(rtenneti): Add unittests for GetSSLInfo which exercise the various ways | |
| 453 // we learn about SSL info (sync vs async vs cached). | |
| 454 bool QuicClientSession::GetSSLInfo(SSLInfo* ssl_info) const { | |
| 455 ssl_info->Reset(); | |
| 456 if (!cert_verify_result_) { | |
| 457 return false; | |
| 458 } | |
| 459 | |
| 460 ssl_info->cert_status = cert_verify_result_->cert_status; | |
| 461 ssl_info->cert = cert_verify_result_->verified_cert; | |
| 462 | |
| 463 // TODO(wtc): Define QUIC "cipher suites". | |
| 464 // Report the TLS cipher suite that most closely resembles the crypto | |
| 465 // parameters of the QUIC connection. | |
| 466 QuicTag aead = crypto_stream_->crypto_negotiated_params().aead; | |
| 467 uint16 cipher_suite; | |
| 468 int security_bits; | |
| 469 switch (aead) { | |
| 470 case kAESG: | |
| 471 cipher_suite = 0xc02f; // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 | |
| 472 security_bits = 128; | |
| 473 break; | |
| 474 case kCC12: | |
| 475 cipher_suite = 0xcc13; // TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 | |
| 476 security_bits = 256; | |
| 477 break; | |
| 478 default: | |
| 479 NOTREACHED(); | |
| 480 return false; | |
| 481 } | |
| 482 int ssl_connection_status = 0; | |
| 483 ssl_connection_status |= cipher_suite; | |
| 484 ssl_connection_status |= | |
| 485 (SSL_CONNECTION_VERSION_QUIC & SSL_CONNECTION_VERSION_MASK) << | |
| 486 SSL_CONNECTION_VERSION_SHIFT; | |
| 487 | |
| 488 ssl_info->public_key_hashes = cert_verify_result_->public_key_hashes; | |
| 489 ssl_info->is_issued_by_known_root = | |
| 490 cert_verify_result_->is_issued_by_known_root; | |
| 491 | |
| 492 ssl_info->connection_status = ssl_connection_status; | |
| 493 ssl_info->client_cert_sent = false; | |
| 494 ssl_info->channel_id_sent = crypto_stream_->WasChannelIDSent(); | |
| 495 ssl_info->security_bits = security_bits; | |
| 496 ssl_info->handshake_type = SSLInfo::HANDSHAKE_FULL; | |
| 497 ssl_info->pinning_failure_log = pinning_failure_log_; | |
| 498 return true; | |
| 499 } | |
| 500 | |
| 501 int QuicClientSession::CryptoConnect(bool require_confirmation, | |
| 502 const CompletionCallback& callback) { | |
| 503 require_confirmation_ = require_confirmation; | |
| 504 handshake_start_ = base::TimeTicks::Now(); | |
| 505 RecordHandshakeState(STATE_STARTED); | |
| 506 DCHECK(flow_controller()); | |
| 507 crypto_stream_->CryptoConnect(); | |
| 508 | |
| 509 if (IsCryptoHandshakeConfirmed()) | |
| 510 return OK; | |
| 511 | |
| 512 // Unless we require handshake confirmation, activate the session if | |
| 513 // we have established initial encryption. | |
| 514 if (!require_confirmation_ && IsEncryptionEstablished()) { | |
| 515 // To mitigate the effects of hanging 0-RTT connections, set up a timer to | |
| 516 // cancel any requests, if the handshake takes too long. | |
| 517 task_runner_->PostDelayedTask( | |
| 518 FROM_HERE, | |
| 519 base::Bind(&QuicClientSession::OnConnectTimeout, | |
| 520 weak_factory_.GetWeakPtr()), | |
| 521 base::TimeDelta::FromMilliseconds(k0RttHandshakeTimeoutMs)); | |
| 522 return OK; | |
| 523 | |
| 524 } | |
| 525 | |
| 526 callback_ = callback; | |
| 527 return ERR_IO_PENDING; | |
| 528 } | |
| 529 | |
| 530 int QuicClientSession::ResumeCryptoConnect(const CompletionCallback& callback) { | |
| 531 | |
| 532 if (IsCryptoHandshakeConfirmed()) | |
| 533 return OK; | |
| 534 | |
| 535 if (!connection()->connected()) | |
| 536 return ERR_QUIC_HANDSHAKE_FAILED; | |
| 537 | |
| 538 callback_ = callback; | |
| 539 return ERR_IO_PENDING; | |
| 540 } | |
| 541 | |
| 542 int QuicClientSession::GetNumSentClientHellos() const { | |
| 543 return crypto_stream_->num_sent_client_hellos(); | |
| 544 } | |
| 545 | |
| 546 bool QuicClientSession::CanPool(const std::string& hostname, | |
| 547 PrivacyMode privacy_mode) const { | |
| 548 DCHECK(connection()->connected()); | |
| 549 if (privacy_mode != server_id_.privacy_mode()) { | |
| 550 // Privacy mode must always match. | |
| 551 return false; | |
| 552 } | |
| 553 SSLInfo ssl_info; | |
| 554 if (!GetSSLInfo(&ssl_info) || !ssl_info.cert.get()) { | |
| 555 // We can always pool with insecure QUIC sessions. | |
| 556 return true; | |
| 557 } | |
| 558 | |
| 559 return SpdySession::CanPool(transport_security_state_, ssl_info, | |
| 560 server_id_.host(), hostname); | |
| 561 } | |
| 562 | |
| 563 QuicDataStream* QuicClientSession::CreateIncomingDataStream( | |
| 564 QuicStreamId id) { | |
| 565 DLOG(ERROR) << "Server push not supported"; | |
| 566 return nullptr; | |
| 567 } | |
| 568 | |
| 569 void QuicClientSession::CloseStream(QuicStreamId stream_id) { | |
| 570 ReliableQuicStream* stream = GetStream(stream_id); | |
| 571 if (stream) { | |
| 572 logger_->UpdateReceivedFrameCounts( | |
| 573 stream_id, stream->num_frames_received(), | |
| 574 stream->num_duplicate_frames_received()); | |
| 575 } | |
| 576 QuicSession::CloseStream(stream_id); | |
| 577 OnClosedStream(); | |
| 578 } | |
| 579 | |
| 580 void QuicClientSession::SendRstStream(QuicStreamId id, | |
| 581 QuicRstStreamErrorCode error, | |
| 582 QuicStreamOffset bytes_written) { | |
| 583 QuicSession::SendRstStream(id, error, bytes_written); | |
| 584 OnClosedStream(); | |
| 585 } | |
| 586 | |
| 587 void QuicClientSession::OnClosedStream() { | |
| 588 if (GetNumOpenStreams() < get_max_open_streams() && | |
| 589 !stream_requests_.empty() && | |
| 590 crypto_stream_->encryption_established() && | |
| 591 !goaway_received() && | |
| 592 !going_away_ && | |
| 593 connection()->connected()) { | |
| 594 StreamRequest* request = stream_requests_.front(); | |
| 595 stream_requests_.pop_front(); | |
| 596 request->OnRequestCompleteSuccess(CreateOutgoingReliableStreamImpl()); | |
| 597 } | |
| 598 | |
| 599 if (GetNumOpenStreams() == 0) { | |
| 600 stream_factory_->OnIdleSession(this); | |
| 601 } | |
| 602 } | |
| 603 | |
| 604 void QuicClientSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { | |
| 605 if (!callback_.is_null() && | |
| 606 (!require_confirmation_ || | |
| 607 event == HANDSHAKE_CONFIRMED || event == ENCRYPTION_REESTABLISHED)) { | |
| 608 // TODO(rtenneti): Currently for all CryptoHandshakeEvent events, callback_ | |
| 609 // could be called because there are no error events in CryptoHandshakeEvent | |
| 610 // enum. If error events are added to CryptoHandshakeEvent, then the | |
| 611 // following code needs to changed. | |
| 612 base::ResetAndReturn(&callback_).Run(OK); | |
| 613 } | |
| 614 if (event == HANDSHAKE_CONFIRMED) { | |
| 615 UMA_HISTOGRAM_TIMES("Net.QuicSession.HandshakeConfirmedTime", | |
| 616 base::TimeTicks::Now() - handshake_start_); | |
| 617 if (server_info_) { | |
| 618 // Track how long it has taken to finish handshake once we start waiting | |
| 619 // for reading of QUIC server information from disk cache. We could use | |
| 620 // this data to compare total time taken if we were to cancel the disk | |
| 621 // cache read vs waiting for the read to complete. | |
| 622 base::TimeTicks wait_for_data_start_time = | |
| 623 server_info_->wait_for_data_start_time(); | |
| 624 if (!wait_for_data_start_time.is_null()) { | |
| 625 UMA_HISTOGRAM_TIMES( | |
| 626 "Net.QuicServerInfo.WaitForDataReady.HandshakeConfirmedTime", | |
| 627 base::TimeTicks::Now() - wait_for_data_start_time); | |
| 628 } | |
| 629 } | |
| 630 | |
| 631 ObserverSet::iterator it = observers_.begin(); | |
| 632 while (it != observers_.end()) { | |
| 633 Observer* observer = *it; | |
| 634 ++it; | |
| 635 observer->OnCryptoHandshakeConfirmed(); | |
| 636 } | |
| 637 if (server_info_) | |
| 638 server_info_->OnExternalCacheHit(); | |
| 639 } | |
| 640 QuicSession::OnCryptoHandshakeEvent(event); | |
| 641 } | |
| 642 | |
| 643 void QuicClientSession::OnCryptoHandshakeMessageSent( | |
| 644 const CryptoHandshakeMessage& message) { | |
| 645 logger_->OnCryptoHandshakeMessageSent(message); | |
| 646 } | |
| 647 | |
| 648 void QuicClientSession::OnCryptoHandshakeMessageReceived( | |
| 649 const CryptoHandshakeMessage& message) { | |
| 650 logger_->OnCryptoHandshakeMessageReceived(message); | |
| 651 } | |
| 652 | |
| 653 void QuicClientSession::OnConnectionClosed(QuicErrorCode error, | |
| 654 bool from_peer) { | |
| 655 DCHECK(!connection()->connected()); | |
| 656 logger_->OnConnectionClosed(error, from_peer); | |
| 657 if (from_peer) { | |
| 658 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
| 659 "Net.QuicSession.ConnectionCloseErrorCodeServer", error); | |
| 660 } else { | |
| 661 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
| 662 "Net.QuicSession.ConnectionCloseErrorCodeClient", error); | |
| 663 } | |
| 664 | |
| 665 if (error == QUIC_CONNECTION_TIMED_OUT) { | |
| 666 UMA_HISTOGRAM_COUNTS( | |
| 667 "Net.QuicSession.ConnectionClose.NumOpenStreams.TimedOut", | |
| 668 GetNumOpenStreams()); | |
| 669 if (IsCryptoHandshakeConfirmed()) { | |
| 670 if (GetNumOpenStreams() > 0) { | |
| 671 UMA_HISTOGRAM_BOOLEAN( | |
| 672 "Net.QuicSession.TimedOutWithOpenStreams.HasUnackedPackets", | |
| 673 connection()->sent_packet_manager().HasUnackedPackets()); | |
| 674 UMA_HISTOGRAM_COUNTS( | |
| 675 "Net.QuicSession.TimedOutWithOpenStreams.ConsecutiveRTOCount", | |
| 676 connection()->sent_packet_manager().consecutive_rto_count()); | |
| 677 UMA_HISTOGRAM_COUNTS( | |
| 678 "Net.QuicSession.TimedOutWithOpenStreams.ConsecutiveTLPCount", | |
| 679 connection()->sent_packet_manager().consecutive_tlp_count()); | |
| 680 } | |
| 681 if (connection()->sent_packet_manager().HasUnackedPackets()) { | |
| 682 UMA_HISTOGRAM_TIMES( | |
| 683 "Net.QuicSession.LocallyTimedOutWithOpenStreams." | |
| 684 "TimeSinceLastReceived.UnackedPackets", | |
| 685 NetworkActivityMonitor::GetInstance()->GetTimeSinceLastReceived()); | |
| 686 } else { | |
| 687 UMA_HISTOGRAM_TIMES( | |
| 688 "Net.QuicSession.LocallyTimedOutWithOpenStreams." | |
| 689 "TimeSinceLastReceived.NoUnackedPackets", | |
| 690 NetworkActivityMonitor::GetInstance()->GetTimeSinceLastReceived()); | |
| 691 } | |
| 692 | |
| 693 } else { | |
| 694 UMA_HISTOGRAM_COUNTS( | |
| 695 "Net.QuicSession.ConnectionClose.NumOpenStreams.HandshakeTimedOut", | |
| 696 GetNumOpenStreams()); | |
| 697 UMA_HISTOGRAM_COUNTS( | |
| 698 "Net.QuicSession.ConnectionClose.NumTotalStreams.HandshakeTimedOut", | |
| 699 num_total_streams_); | |
| 700 } | |
| 701 } | |
| 702 | |
| 703 if (!IsCryptoHandshakeConfirmed()) { | |
| 704 if (error == QUIC_PUBLIC_RESET) { | |
| 705 RecordHandshakeFailureReason(HANDSHAKE_FAILURE_PUBLIC_RESET); | |
| 706 } else if (connection()->GetStats().packets_received == 0) { | |
| 707 RecordHandshakeFailureReason(HANDSHAKE_FAILURE_BLACK_HOLE); | |
| 708 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
| 709 "Net.QuicSession.ConnectionClose.HandshakeFailureBlackHole.QuicError", | |
| 710 error); | |
| 711 } else { | |
| 712 RecordHandshakeFailureReason(HANDSHAKE_FAILURE_UNKNOWN); | |
| 713 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
| 714 "Net.QuicSession.ConnectionClose.HandshakeFailureUnknown.QuicError", | |
| 715 error); | |
| 716 } | |
| 717 } | |
| 718 | |
| 719 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.QuicVersion", | |
| 720 connection()->version()); | |
| 721 NotifyFactoryOfSessionGoingAway(); | |
| 722 if (!callback_.is_null()) { | |
| 723 base::ResetAndReturn(&callback_).Run(ERR_QUIC_PROTOCOL_ERROR); | |
| 724 } | |
| 725 socket_->Close(); | |
| 726 QuicSession::OnConnectionClosed(error, from_peer); | |
| 727 DCHECK(streams()->empty()); | |
| 728 CloseAllStreams(ERR_UNEXPECTED); | |
| 729 CloseAllObservers(ERR_UNEXPECTED); | |
| 730 NotifyFactoryOfSessionClosedLater(); | |
| 731 } | |
| 732 | |
| 733 void QuicClientSession::OnSuccessfulVersionNegotiation( | |
| 734 const QuicVersion& version) { | |
| 735 logger_->OnSuccessfulVersionNegotiation(version); | |
| 736 QuicSession::OnSuccessfulVersionNegotiation(version); | |
| 737 } | |
| 738 | |
| 739 void QuicClientSession::OnProofValid( | |
| 740 const QuicCryptoClientConfig::CachedState& cached) { | |
| 741 DCHECK(cached.proof_valid()); | |
| 742 | |
| 743 if (!server_info_) { | |
| 744 return; | |
| 745 } | |
| 746 | |
| 747 QuicServerInfo::State* state = server_info_->mutable_state(); | |
| 748 | |
| 749 state->server_config = cached.server_config(); | |
| 750 state->source_address_token = cached.source_address_token(); | |
| 751 state->server_config_sig = cached.signature(); | |
| 752 state->certs = cached.certs(); | |
| 753 | |
| 754 server_info_->Persist(); | |
| 755 } | |
| 756 | |
| 757 void QuicClientSession::OnProofVerifyDetailsAvailable( | |
| 758 const ProofVerifyDetails& verify_details) { | |
| 759 const ProofVerifyDetailsChromium* verify_details_chromium = | |
| 760 reinterpret_cast<const ProofVerifyDetailsChromium*>(&verify_details); | |
| 761 CertVerifyResult* result_copy = new CertVerifyResult; | |
| 762 result_copy->CopyFrom(verify_details_chromium->cert_verify_result); | |
| 763 cert_verify_result_.reset(result_copy); | |
| 764 pinning_failure_log_ = verify_details_chromium->pinning_failure_log; | |
| 765 logger_->OnCertificateVerified(*cert_verify_result_); | |
| 766 } | |
| 767 | |
| 768 void QuicClientSession::StartReading() { | |
| 769 if (read_pending_) { | |
| 770 return; | |
| 771 } | |
| 772 read_pending_ = true; | |
| 773 int rv = socket_->Read(read_buffer_.get(), | |
| 774 read_buffer_->size(), | |
| 775 base::Bind(&QuicClientSession::OnReadComplete, | |
| 776 weak_factory_.GetWeakPtr())); | |
| 777 UMA_HISTOGRAM_BOOLEAN("Net.QuicSession.AsyncRead", rv == ERR_IO_PENDING); | |
| 778 if (rv == ERR_IO_PENDING) { | |
| 779 num_packets_read_ = 0; | |
| 780 return; | |
| 781 } | |
| 782 | |
| 783 if (++num_packets_read_ > 32) { | |
| 784 num_packets_read_ = 0; | |
| 785 // Data was read, process it. | |
| 786 // Schedule the work through the message loop to 1) prevent infinite | |
| 787 // recursion and 2) avoid blocking the thread for too long. | |
| 788 base::MessageLoop::current()->PostTask( | |
| 789 FROM_HERE, | |
| 790 base::Bind(&QuicClientSession::OnReadComplete, | |
| 791 weak_factory_.GetWeakPtr(), rv)); | |
| 792 } else { | |
| 793 OnReadComplete(rv); | |
| 794 } | |
| 795 } | |
| 796 | |
| 797 void QuicClientSession::CloseSessionOnError(int error) { | |
| 798 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.CloseSessionOnError", -error); | |
| 799 CloseSessionOnErrorInner(error, QUIC_INTERNAL_ERROR); | |
| 800 NotifyFactoryOfSessionClosed(); | |
| 801 } | |
| 802 | |
| 803 void QuicClientSession::CloseSessionOnErrorInner(int net_error, | |
| 804 QuicErrorCode quic_error) { | |
| 805 if (!callback_.is_null()) { | |
| 806 base::ResetAndReturn(&callback_).Run(net_error); | |
| 807 } | |
| 808 CloseAllStreams(net_error); | |
| 809 CloseAllObservers(net_error); | |
| 810 net_log_.AddEvent( | |
| 811 NetLog::TYPE_QUIC_SESSION_CLOSE_ON_ERROR, | |
| 812 NetLog::IntegerCallback("net_error", net_error)); | |
| 813 | |
| 814 if (connection()->connected()) | |
| 815 connection()->CloseConnection(quic_error, false); | |
| 816 DCHECK(!connection()->connected()); | |
| 817 } | |
| 818 | |
| 819 void QuicClientSession::CloseAllStreams(int net_error) { | |
| 820 while (!streams()->empty()) { | |
| 821 ReliableQuicStream* stream = streams()->begin()->second; | |
| 822 QuicStreamId id = stream->id(); | |
| 823 static_cast<QuicReliableClientStream*>(stream)->OnError(net_error); | |
| 824 CloseStream(id); | |
| 825 } | |
| 826 } | |
| 827 | |
| 828 void QuicClientSession::CloseAllObservers(int net_error) { | |
| 829 while (!observers_.empty()) { | |
| 830 Observer* observer = *observers_.begin(); | |
| 831 observers_.erase(observer); | |
| 832 observer->OnSessionClosed(net_error); | |
| 833 } | |
| 834 } | |
| 835 | |
| 836 base::Value* QuicClientSession::GetInfoAsValue( | |
| 837 const std::set<HostPortPair>& aliases) { | |
| 838 base::DictionaryValue* dict = new base::DictionaryValue(); | |
| 839 dict->SetString("version", QuicVersionToString(connection()->version())); | |
| 840 dict->SetInteger("open_streams", GetNumOpenStreams()); | |
| 841 base::ListValue* stream_list = new base::ListValue(); | |
| 842 for (base::hash_map<QuicStreamId, QuicDataStream*>::const_iterator it | |
| 843 = streams()->begin(); | |
| 844 it != streams()->end(); | |
| 845 ++it) { | |
| 846 stream_list->Append(new base::StringValue( | |
| 847 base::Uint64ToString(it->second->id()))); | |
| 848 } | |
| 849 dict->Set("active_streams", stream_list); | |
| 850 | |
| 851 dict->SetInteger("total_streams", num_total_streams_); | |
| 852 dict->SetString("peer_address", peer_address().ToString()); | |
| 853 dict->SetString("connection_id", base::Uint64ToString(connection_id())); | |
| 854 dict->SetBoolean("connected", connection()->connected()); | |
| 855 const QuicConnectionStats& stats = connection()->GetStats(); | |
| 856 dict->SetInteger("packets_sent", stats.packets_sent); | |
| 857 dict->SetInteger("packets_received", stats.packets_received); | |
| 858 dict->SetInteger("packets_lost", stats.packets_lost); | |
| 859 SSLInfo ssl_info; | |
| 860 dict->SetBoolean("secure", GetSSLInfo(&ssl_info) && ssl_info.cert.get()); | |
| 861 | |
| 862 base::ListValue* alias_list = new base::ListValue(); | |
| 863 for (std::set<HostPortPair>::const_iterator it = aliases.begin(); | |
| 864 it != aliases.end(); it++) { | |
| 865 alias_list->Append(new base::StringValue(it->ToString())); | |
| 866 } | |
| 867 dict->Set("aliases", alias_list); | |
| 868 | |
| 869 return dict; | |
| 870 } | |
| 871 | |
| 872 base::WeakPtr<QuicClientSession> QuicClientSession::GetWeakPtr() { | |
| 873 return weak_factory_.GetWeakPtr(); | |
| 874 } | |
| 875 | |
| 876 void QuicClientSession::OnReadComplete(int result) { | |
| 877 read_pending_ = false; | |
| 878 if (result == 0) | |
| 879 result = ERR_CONNECTION_CLOSED; | |
| 880 | |
| 881 if (result < 0) { | |
| 882 DVLOG(1) << "Closing session on read error: " << result; | |
| 883 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.ReadError", -result); | |
| 884 NotifyFactoryOfSessionGoingAway(); | |
| 885 CloseSessionOnErrorInner(result, QUIC_PACKET_READ_ERROR); | |
| 886 NotifyFactoryOfSessionClosedLater(); | |
| 887 return; | |
| 888 } | |
| 889 | |
| 890 QuicEncryptedPacket packet(read_buffer_->data(), result); | |
| 891 IPEndPoint local_address; | |
| 892 IPEndPoint peer_address; | |
| 893 socket_->GetLocalAddress(&local_address); | |
| 894 socket_->GetPeerAddress(&peer_address); | |
| 895 // ProcessUdpPacket might result in |this| being deleted, so we | |
| 896 // use a weak pointer to be safe. | |
| 897 connection()->ProcessUdpPacket(local_address, peer_address, packet); | |
| 898 if (!connection()->connected()) { | |
| 899 NotifyFactoryOfSessionClosedLater(); | |
| 900 return; | |
| 901 } | |
| 902 StartReading(); | |
| 903 } | |
| 904 | |
| 905 void QuicClientSession::NotifyFactoryOfSessionGoingAway() { | |
| 906 going_away_ = true; | |
| 907 if (stream_factory_) | |
| 908 stream_factory_->OnSessionGoingAway(this); | |
| 909 } | |
| 910 | |
| 911 void QuicClientSession::NotifyFactoryOfSessionClosedLater() { | |
| 912 if (!streams()->empty()) | |
| 913 RecordUnexpectedOpenStreams(NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER); | |
| 914 | |
| 915 if (!going_away_) | |
| 916 RecordUnexpectedNotGoingAway(NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER); | |
| 917 | |
| 918 going_away_ = true; | |
| 919 DCHECK_EQ(0u, GetNumOpenStreams()); | |
| 920 DCHECK(!connection()->connected()); | |
| 921 base::MessageLoop::current()->PostTask( | |
| 922 FROM_HERE, | |
| 923 base::Bind(&QuicClientSession::NotifyFactoryOfSessionClosed, | |
| 924 weak_factory_.GetWeakPtr())); | |
| 925 } | |
| 926 | |
| 927 void QuicClientSession::NotifyFactoryOfSessionClosed() { | |
| 928 if (!streams()->empty()) | |
| 929 RecordUnexpectedOpenStreams(NOTIFY_FACTORY_OF_SESSION_CLOSED); | |
| 930 | |
| 931 if (!going_away_) | |
| 932 RecordUnexpectedNotGoingAway(NOTIFY_FACTORY_OF_SESSION_CLOSED); | |
| 933 | |
| 934 going_away_ = true; | |
| 935 DCHECK_EQ(0u, GetNumOpenStreams()); | |
| 936 // Will delete |this|. | |
| 937 if (stream_factory_) | |
| 938 stream_factory_->OnSessionClosed(this); | |
| 939 } | |
| 940 | |
| 941 void QuicClientSession::OnConnectTimeout() { | |
| 942 DCHECK(callback_.is_null()); | |
| 943 DCHECK(IsEncryptionEstablished()); | |
| 944 | |
| 945 if (IsCryptoHandshakeConfirmed()) | |
| 946 return; | |
| 947 | |
| 948 // TODO(rch): re-enable this code once beta is cut. | |
| 949 // if (stream_factory_) | |
| 950 // stream_factory_->OnSessionConnectTimeout(this); | |
| 951 // CloseAllStreams(ERR_QUIC_HANDSHAKE_FAILED); | |
| 952 // DCHECK_EQ(0u, GetNumOpenStreams()); | |
| 953 } | |
| 954 | |
| 955 } // namespace net | |
| OLD | NEW |