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 |