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/spdy/spdy_session.h"
9 #include "base/basictypes.h"
10 #include "base/logging.h"
11 #include "base/memory/linked_ptr.h"
12 #include "base/message_loop.h"
13 #include "base/metrics/field_trial.h"
14 #include "base/metrics/stats_counters.h"
15 #include "base/stl_util.h"
16 #include "base/string_number_conversions.h"
17 #include "base/string_util.h"
18 #include "base/stringprintf.h"
19 #include "base/time.h"
20 #include "base/utf_string_conversions.h"
21 #include "base/values.h"
22 #include "crypto/ec_private_key.h"
23 #include "crypto/ec_signature_creator.h"
24 #include "crypto/signature_creator.h"
25 #include "net/base/asn1_util.h"
26 #include "net/base/connection_type_histograms.h"
27 #include "net/base/net_log.h"
28 #include "net/base/net_util.h"
29 #include "net/base/server_bound_cert_service.h"
30 #include "net/http/http_network_session.h"
31 #include "net/http/http_server_properties.h"
32 #include "net/spdy/spdy_frame_builder.h"
33 #include "net/spdy/spdy_http_utils.h"
34 #include "net/spdy/spdy_protocol.h"
35 #include "net/spdy/spdy_session_pool.h"
36 #include "net/spdy/spdy_settings_storage.h"
37 #include "net/spdy/spdy_stream.h"
41 NetLogSpdySynParameter::NetLogSpdySynParameter(
42 const linked_ptr
<SpdyHeaderBlock
>& headers
,
43 SpdyControlFlags flags
,
45 SpdyStreamId associated_stream
)
49 associated_stream_(associated_stream
) {
52 NetLogSpdySynParameter::~NetLogSpdySynParameter() {
55 Value
* NetLogSpdySynParameter::ToValue() const {
56 DictionaryValue
* dict
= new DictionaryValue();
57 ListValue
* headers_list
= new ListValue();
58 for (SpdyHeaderBlock::const_iterator it
= headers_
->begin();
59 it
!= headers_
->end(); ++it
) {
60 headers_list
->Append(new StringValue(base::StringPrintf(
61 "%s: %s", it
->first
.c_str(), it
->second
.c_str())));
63 dict
->SetInteger("flags", flags_
);
64 dict
->Set("headers", headers_list
);
65 dict
->SetInteger("id", id_
);
66 if (associated_stream_
)
67 dict
->SetInteger("associated_stream", associated_stream_
);
71 NetLogSpdyCredentialParameter::NetLogSpdyCredentialParameter(
73 const std::string
& origin
)
78 NetLogSpdyCredentialParameter::~NetLogSpdyCredentialParameter() {
81 Value
* NetLogSpdyCredentialParameter::ToValue() const {
82 DictionaryValue
* dict
= new DictionaryValue();
83 dict
->SetInteger("slot", slot_
);
84 dict
->SetString("origin", origin_
);
88 NetLogSpdySessionCloseParameter::NetLogSpdySessionCloseParameter(
90 const std::string
& description
)
92 description_(description
) {}
94 NetLogSpdySessionCloseParameter::~NetLogSpdySessionCloseParameter() {
97 Value
* NetLogSpdySessionCloseParameter::ToValue() const {
98 DictionaryValue
* dict
= new DictionaryValue();
99 dict
->SetInteger("status", status_
);
100 dict
->SetString("description", description_
);
106 const int kReadBufferSize
= 8 * 1024;
107 const int kDefaultConnectionAtRiskOfLossSeconds
= 10;
108 const int kTrailingPingDelayTimeSeconds
= 1;
109 const int kHungIntervalSeconds
= 10;
111 class NetLogSpdySessionParameter
: public NetLog::EventParameters
{
113 NetLogSpdySessionParameter(const HostPortProxyPair
& host_pair
)
114 : host_pair_(host_pair
) {}
115 virtual Value
* ToValue() const {
116 DictionaryValue
* dict
= new DictionaryValue();
117 dict
->Set("host", new StringValue(host_pair_
.first
.ToString()));
118 dict
->Set("proxy", new StringValue(host_pair_
.second
.ToPacString()));
122 const HostPortProxyPair host_pair_
;
123 DISALLOW_COPY_AND_ASSIGN(NetLogSpdySessionParameter
);
126 class NetLogSpdySettingsParameter
: public NetLog::EventParameters
{
128 explicit NetLogSpdySettingsParameter(const SpdySettings
& settings
)
129 : settings_(settings
) {}
131 virtual Value
* ToValue() const {
132 DictionaryValue
* dict
= new DictionaryValue();
133 ListValue
* settings
= new ListValue();
134 for (SpdySettings::const_iterator it
= settings_
.begin();
135 it
!= settings_
.end(); ++it
) {
136 settings
->Append(new StringValue(
137 base::StringPrintf("[%u:%u]", it
->first
.id(), it
->second
)));
139 dict
->Set("settings", settings
);
144 ~NetLogSpdySettingsParameter() {}
145 const SpdySettings settings_
;
147 DISALLOW_COPY_AND_ASSIGN(NetLogSpdySettingsParameter
);
150 class NetLogSpdyWindowUpdateParameter
: public NetLog::EventParameters
{
152 NetLogSpdyWindowUpdateParameter(SpdyStreamId stream_id
, int32 delta
)
153 : stream_id_(stream_id
), delta_(delta
) {}
155 virtual Value
* ToValue() const {
156 DictionaryValue
* dict
= new DictionaryValue();
157 dict
->SetInteger("stream_id", static_cast<int>(stream_id_
));
158 dict
->SetInteger("delta", delta_
);
163 ~NetLogSpdyWindowUpdateParameter() {}
164 const SpdyStreamId stream_id_
;
167 DISALLOW_COPY_AND_ASSIGN(NetLogSpdyWindowUpdateParameter
);
170 class NetLogSpdyDataParameter
: public NetLog::EventParameters
{
172 NetLogSpdyDataParameter(SpdyStreamId stream_id
,
175 : stream_id_(stream_id
), size_(size
), flags_(flags
) {}
177 virtual Value
* ToValue() const {
178 DictionaryValue
* dict
= new DictionaryValue();
179 dict
->SetInteger("stream_id", static_cast<int>(stream_id_
));
180 dict
->SetInteger("size", size_
);
181 dict
->SetInteger("flags", static_cast<int>(flags_
));
186 ~NetLogSpdyDataParameter() {}
187 const SpdyStreamId stream_id_
;
189 const SpdyDataFlags flags_
;
191 DISALLOW_COPY_AND_ASSIGN(NetLogSpdyDataParameter
);
194 class NetLogSpdyRstParameter
: public NetLog::EventParameters
{
196 NetLogSpdyRstParameter(SpdyStreamId stream_id
,
198 const std::string
& description
)
199 : stream_id_(stream_id
),
201 description_(description
) {}
203 virtual Value
* ToValue() const {
204 DictionaryValue
* dict
= new DictionaryValue();
205 dict
->SetInteger("stream_id", static_cast<int>(stream_id_
));
206 dict
->SetInteger("status", status_
);
207 dict
->SetString("description", description_
);
212 ~NetLogSpdyRstParameter() {}
213 const SpdyStreamId stream_id_
;
215 const std::string description_
;
217 DISALLOW_COPY_AND_ASSIGN(NetLogSpdyRstParameter
);
220 class NetLogSpdyPingParameter
: public NetLog::EventParameters
{
222 explicit NetLogSpdyPingParameter(uint32 unique_id
, const std::string
& type
)
223 : unique_id_(unique_id
),
226 virtual Value
* ToValue() const {
227 DictionaryValue
* dict
= new DictionaryValue();
228 dict
->SetInteger("unique_id", unique_id_
);
229 dict
->SetString("type", type_
);
234 ~NetLogSpdyPingParameter() {}
235 const uint32 unique_id_
;
236 const std::string type_
;
238 DISALLOW_COPY_AND_ASSIGN(NetLogSpdyPingParameter
);
241 class NetLogSpdyGoAwayParameter
: public NetLog::EventParameters
{
243 NetLogSpdyGoAwayParameter(SpdyStreamId last_stream_id
,
245 int unclaimed_streams
)
246 : last_stream_id_(last_stream_id
),
247 active_streams_(active_streams
),
248 unclaimed_streams_(unclaimed_streams
) {}
250 virtual Value
* ToValue() const {
251 DictionaryValue
* dict
= new DictionaryValue();
252 dict
->SetInteger("last_accepted_stream_id",
253 static_cast<int>(last_stream_id_
));
254 dict
->SetInteger("active_streams", active_streams_
);
255 dict
->SetInteger("unclaimed_streams", unclaimed_streams_
);
260 ~NetLogSpdyGoAwayParameter() {}
261 const SpdyStreamId last_stream_id_
;
262 const int active_streams_
;
263 const int unclaimed_streams_
;
265 DISALLOW_COPY_AND_ASSIGN(NetLogSpdyGoAwayParameter
);
268 SSLClientSocket::NextProto g_default_protocol
= SSLClientSocket::kProtoUnknown
;
269 size_t g_init_max_concurrent_streams
= 10;
270 size_t g_max_concurrent_stream_limit
= 256;
271 bool g_enable_ping_based_connection_checking
= true;
276 void SpdySession::set_default_protocol(
277 SSLClientSocket::NextProto default_protocol
) {
278 g_default_protocol
= default_protocol
;
282 void SpdySession::set_max_concurrent_streams(size_t value
) {
283 g_max_concurrent_stream_limit
= value
;
287 void SpdySession::set_enable_ping_based_connection_checking(bool enable
) {
288 g_enable_ping_based_connection_checking
= enable
;
292 void SpdySession::set_init_max_concurrent_streams(size_t value
) {
293 g_init_max_concurrent_streams
=
294 std::min(value
, g_max_concurrent_stream_limit
);
298 void SpdySession::ResetStaticSettingsToInit() {
299 // WARNING: These must match the initializers above.
300 g_default_protocol
= SSLClientSocket::kProtoUnknown
;
301 g_init_max_concurrent_streams
= 10;
302 g_max_concurrent_stream_limit
= 256;
303 g_enable_ping_based_connection_checking
= true;
306 SpdySession::SpdySession(const HostPortProxyPair
& host_port_proxy_pair
,
307 SpdySessionPool
* spdy_session_pool
,
308 HttpServerProperties
* http_server_properties
,
309 bool verify_domain_authentication
,
311 : ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
312 host_port_proxy_pair_(host_port_proxy_pair
),
313 spdy_session_pool_(spdy_session_pool
),
314 http_server_properties_(http_server_properties
),
315 connection_(new ClientSocketHandle
),
316 read_buffer_(new IOBuffer(kReadBufferSize
)),
317 read_pending_(false),
318 stream_hi_water_mark_(1), // Always start at 1 for the first stream id.
319 write_pending_(false),
320 delayed_write_pending_(false),
322 certificate_error_code_(OK
),
325 max_concurrent_streams_(g_init_max_concurrent_streams
),
326 streams_initiated_count_(0),
327 streams_pushed_count_(0),
328 streams_pushed_and_claimed_count_(0),
329 streams_abandoned_count_(0),
331 sent_settings_(false),
332 received_settings_(false),
336 received_data_time_(base::TimeTicks::Now()),
337 trailing_ping_pending_(false),
338 check_ping_status_pending_(false),
339 need_to_send_ping_(false),
340 flow_control_(false),
341 initial_send_window_size_(kSpdyStreamInitialWindowSize
),
342 initial_recv_window_size_(kSpdyStreamInitialWindowSize
),
343 net_log_(BoundNetLog::Make(net_log
, NetLog::SOURCE_SPDY_SESSION
)),
344 verify_domain_authentication_(verify_domain_authentication
),
345 credential_state_(SpdyCredentialState::kDefaultNumSlots
),
346 connection_at_risk_of_loss_time_(
347 base::TimeDelta::FromSeconds(kDefaultConnectionAtRiskOfLossSeconds
)),
348 trailing_ping_delay_time_(
349 base::TimeDelta::FromSeconds(kTrailingPingDelayTimeSeconds
)),
351 base::TimeDelta::FromSeconds(kHungIntervalSeconds
)) {
352 DCHECK(HttpStreamFactory::spdy_enabled());
354 NetLog::TYPE_SPDY_SESSION
,
356 new NetLogSpdySessionParameter(host_port_proxy_pair_
)));
357 // TODO(mbelshe): consider randomization of the stream_hi_water_mark.
360 SpdySession::PendingCreateStream::~PendingCreateStream() {}
362 SpdySession::CallbackResultPair::~CallbackResultPair() {}
364 SpdySession::~SpdySession() {
365 if (state_
!= CLOSED
) {
368 // Cleanup all the streams.
369 CloseAllStreams(net::ERR_ABORTED
);
372 if (connection_
->is_initialized()) {
373 // With SPDY we can't recycle sockets.
374 connection_
->socket()->Disconnect();
377 // Streams should all be gone now.
378 DCHECK_EQ(0u, num_active_streams());
379 DCHECK_EQ(0u, num_unclaimed_pushed_streams());
381 DCHECK(pending_callback_map_
.empty());
385 net_log_
.EndEvent(NetLog::TYPE_SPDY_SESSION
, NULL
);
388 net::Error
SpdySession::InitializeWithSocket(
389 ClientSocketHandle
* connection
,
391 int certificate_error_code
) {
392 base::StatsCounter
spdy_sessions("spdy.sessions");
393 spdy_sessions
.Increment();
396 connection_
.reset(connection
);
397 connection_
->AddLayeredPool(this);
398 is_secure_
= is_secure
;
399 certificate_error_code_
= certificate_error_code
;
401 SSLClientSocket::NextProto protocol
= g_default_protocol
;
403 SSLClientSocket
* ssl_socket
= GetSSLClientSocket();
405 SSLClientSocket::NextProto protocol_negotiated
=
406 ssl_socket
->protocol_negotiated();
407 if (protocol_negotiated
!= SSLClientSocket::kProtoUnknown
) {
408 protocol
= protocol_negotiated
;
411 if (ssl_socket
->WasDomainBoundCertSent()) {
412 // According to the SPDY spec, the credential associated with the TLS
413 // connection is stored in slot[0].
414 credential_state_
.SetHasCredential(host_port_pair());
418 DCHECK(protocol
>= SSLClientSocket::kProtoSPDY2
);
419 DCHECK(protocol
<= SSLClientSocket::kProtoSPDY3
);
420 int version
= (protocol
== SSLClientSocket::kProtoSPDY3
) ? 3 : 2;
421 flow_control_
= (protocol
>= SSLClientSocket::kProtoSPDY21
);
423 buffered_spdy_framer_
.reset(new BufferedSpdyFramer(version
));
424 buffered_spdy_framer_
->set_visitor(this);
427 // Write out any data that we might have to send, such as the settings frame.
429 net::Error error
= ReadSocket();
430 if (error
== ERR_IO_PENDING
)
435 bool SpdySession::VerifyDomainAuthentication(const std::string
& domain
) {
436 if (!verify_domain_authentication_
)
439 if (state_
!= CONNECTED
)
443 bool was_npn_negotiated
;
444 SSLClientSocket::NextProto protocol_negotiated
=
445 SSLClientSocket::kProtoUnknown
;
446 if (!GetSSLInfo(&ssl_info
, &was_npn_negotiated
, &protocol_negotiated
))
447 return true; // This is not a secure session, so all domains are okay.
449 return !ssl_info
.client_cert_sent
&& ssl_info
.cert
->VerifyNameMatch(domain
);
452 int SpdySession::GetPushStream(
454 scoped_refptr
<SpdyStream
>* stream
,
455 const BoundNetLog
& stream_net_log
) {
456 CHECK_NE(state_
, CLOSED
);
460 // Don't allow access to secure push streams over an unauthenticated, but
461 // encrypted SSL socket.
462 if (is_secure_
&& certificate_error_code_
!= OK
&&
463 (url
.SchemeIs("https") || url
.SchemeIs("wss"))) {
465 static_cast<net::Error
>(certificate_error_code_
),
467 "Tried to get SPDY stream for secure content over an unauthenticated "
469 return ERR_SPDY_PROTOCOL_ERROR
;
472 *stream
= GetActivePushStream(url
.spec());
474 DCHECK(streams_pushed_and_claimed_count_
< streams_pushed_count_
);
475 streams_pushed_and_claimed_count_
++;
481 int SpdySession::CreateStream(
483 RequestPriority priority
,
484 scoped_refptr
<SpdyStream
>* spdy_stream
,
485 const BoundNetLog
& stream_net_log
,
486 const CompletionCallback
& callback
) {
487 if (!max_concurrent_streams_
||
488 active_streams_
.size() < max_concurrent_streams_
) {
489 return CreateStreamImpl(url
, priority
, spdy_stream
, stream_net_log
);
493 net_log().AddEvent(NetLog::TYPE_SPDY_SESSION_STALLED_MAX_STREAMS
, NULL
);
494 create_stream_queues_
[priority
].push(
495 PendingCreateStream(url
, priority
, spdy_stream
,
496 stream_net_log
, callback
));
497 return ERR_IO_PENDING
;
500 void SpdySession::ProcessPendingCreateStreams() {
501 while (!max_concurrent_streams_
||
502 active_streams_
.size() < max_concurrent_streams_
) {
503 bool no_pending_create_streams
= true;
504 for (int i
= 0;i
< NUM_PRIORITIES
;++i
) {
505 if (!create_stream_queues_
[i
].empty()) {
506 PendingCreateStream pending_create
= create_stream_queues_
[i
].front();
507 create_stream_queues_
[i
].pop();
508 no_pending_create_streams
= false;
509 int error
= CreateStreamImpl(*pending_create
.url
,
510 pending_create
.priority
,
511 pending_create
.spdy_stream
,
512 *pending_create
.stream_net_log
);
513 scoped_refptr
<SpdyStream
>* stream
= pending_create
.spdy_stream
;
514 DCHECK(!ContainsKey(pending_callback_map_
, stream
));
515 pending_callback_map_
.insert(std::make_pair(stream
,
516 CallbackResultPair(pending_create
.callback
, error
)));
517 MessageLoop::current()->PostTask(
519 base::Bind(&SpdySession::InvokeUserStreamCreationCallback
,
520 weak_factory_
.GetWeakPtr(), stream
));
524 if (no_pending_create_streams
)
525 return; // there were no streams in any queue
529 void SpdySession::CancelPendingCreateStreams(
530 const scoped_refptr
<SpdyStream
>* spdy_stream
) {
531 PendingCallbackMap::iterator it
= pending_callback_map_
.find(spdy_stream
);
532 if (it
!= pending_callback_map_
.end()) {
533 pending_callback_map_
.erase(it
);
537 for (int i
= 0;i
< NUM_PRIORITIES
;++i
) {
538 PendingCreateStreamQueue tmp
;
539 // Make a copy removing this trans
540 while (!create_stream_queues_
[i
].empty()) {
541 PendingCreateStream pending_create
= create_stream_queues_
[i
].front();
542 create_stream_queues_
[i
].pop();
543 if (pending_create
.spdy_stream
!= spdy_stream
)
544 tmp
.push(pending_create
);
547 while (!tmp
.empty()) {
548 create_stream_queues_
[i
].push(tmp
.front());
554 int SpdySession::CreateStreamImpl(
556 RequestPriority priority
,
557 scoped_refptr
<SpdyStream
>* spdy_stream
,
558 const BoundNetLog
& stream_net_log
) {
559 // Make sure that we don't try to send https/wss over an unauthenticated, but
560 // encrypted SSL socket.
561 if (is_secure_
&& certificate_error_code_
!= OK
&&
562 (url
.SchemeIs("https") || url
.SchemeIs("wss"))) {
564 static_cast<net::Error
>(certificate_error_code_
),
566 "Tried to create SPDY stream for secure content over an "
567 "unauthenticated session.");
568 return ERR_SPDY_PROTOCOL_ERROR
;
571 const std::string
& path
= url
.PathForRequest();
573 const SpdyStreamId stream_id
= GetNewStreamId();
575 *spdy_stream
= new SpdyStream(this,
579 const scoped_refptr
<SpdyStream
>& stream
= *spdy_stream
;
581 stream
->set_priority(priority
);
582 stream
->set_path(path
);
583 stream
->set_send_window_size(initial_send_window_size_
);
584 stream
->set_recv_window_size(initial_recv_window_size_
);
585 ActivateStream(stream
);
587 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyPriorityCount",
588 static_cast<int>(priority
), 0, 10, 11);
590 // TODO(mbelshe): Optimize memory allocations
591 DCHECK(priority
>= net::HIGHEST
&& priority
< net::NUM_PRIORITIES
);
593 DCHECK_EQ(active_streams_
[stream_id
].get(), stream
.get());
597 bool SpdySession::NeedsCredentials(const HostPortPair
& origin
) const {
600 SSLClientSocket
* ssl_socket
= GetSSLClientSocket();
601 if (ssl_socket
->protocol_negotiated() < SSLClientSocket::kProtoSPDY3
)
603 if (!ssl_socket
->WasDomainBoundCertSent())
605 return !credential_state_
.HasCredential(origin
);
608 void SpdySession::AddPooledAlias(const HostPortProxyPair
& alias
) {
609 pooled_aliases_
.insert(alias
);
612 int SpdySession::GetProtocolVersion() const {
613 DCHECK(buffered_spdy_framer_
.get());
614 return buffered_spdy_framer_
->protocol_version();
617 int SpdySession::WriteSynStream(
618 SpdyStreamId stream_id
,
619 RequestPriority priority
,
620 SpdyControlFlags flags
,
621 const linked_ptr
<SpdyHeaderBlock
>& headers
) {
623 if (!IsStreamActive(stream_id
))
624 return ERR_INVALID_SPDY_STREAM
;
625 const scoped_refptr
<SpdyStream
>& stream
= active_streams_
[stream_id
];
626 CHECK_EQ(stream
->stream_id(), stream_id
);
628 SendPrefacePingIfNoneInFlight();
630 DCHECK(buffered_spdy_framer_
.get());
631 scoped_ptr
<SpdySynStreamControlFrame
> syn_frame(
632 buffered_spdy_framer_
->CreateSynStream(
634 ConvertRequestPriorityToSpdyPriority(priority
),
635 flags
, false, headers
.get()));
636 QueueFrame(syn_frame
.get(), priority
, stream
);
638 base::StatsCounter
spdy_requests("spdy.requests");
639 spdy_requests
.Increment();
640 streams_initiated_count_
++;
642 if (net_log().IsLoggingAllEvents()) {
644 NetLog::TYPE_SPDY_SESSION_SYN_STREAM
,
646 new NetLogSpdySynParameter(headers
, flags
, stream_id
, 0)));
649 // Some servers don't like too many pings, so we limit our current sending to
650 // no more than two pings for any syn frame or data frame sent. To do this,
651 // we avoid ever setting this to true unless we send a syn (which we have just
652 // done) or data frame. This approach may change over time as servers change
653 // their responses to pings.
654 need_to_send_ping_
= true;
656 return ERR_IO_PENDING
;
659 int SpdySession::WriteCredentialFrame(const std::string
& origin
,
660 SSLClientCertType type
,
661 const std::string
& key
,
662 const std::string
& cert
,
663 RequestPriority priority
) {
665 unsigned char secret
[32]; // 32 bytes from the spec
666 GetSSLClientSocket()->ExportKeyingMaterial("SPDY certificate proof",
668 secret
, arraysize(secret
));
670 // Convert the key string into a vector<unit8>
671 std::vector
<uint8
> key_data
;
672 for (size_t i
= 0; i
< key
.length(); i
++) {
673 key_data
.push_back(key
[i
]);
676 std::vector
<uint8
> proof
;
678 case CLIENT_CERT_ECDSA_SIGN
: {
679 base::StringPiece spki_piece
;
680 asn1::ExtractSPKIFromDERCert(cert
, &spki_piece
);
681 std::vector
<uint8
> spki(spki_piece
.data(),
682 spki_piece
.data() + spki_piece
.size());
683 scoped_ptr
<crypto::ECPrivateKey
> private_key(
684 crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo(
685 ServerBoundCertService::kEPKIPassword
, key_data
, spki
));
686 scoped_ptr
<crypto::ECSignatureCreator
> creator(
687 crypto::ECSignatureCreator::Create(private_key
.get()));
688 creator
->Sign(secret
, arraysize(secret
), &proof
);
695 SpdyCredential credential
;
696 GURL
origin_url(origin
);
698 credential_state_
.SetHasCredential(HostPortPair::FromURL(origin_url
));
699 credential
.certs
.push_back(cert
);
700 credential
.proof
.assign(proof
.begin(), proof
.end());
702 DCHECK(buffered_spdy_framer_
.get());
703 scoped_ptr
<SpdyCredentialControlFrame
> credential_frame(
704 buffered_spdy_framer_
->CreateCredentialFrame(credential
));
705 QueueFrame(credential_frame
.get(), priority
, NULL
);
707 if (net_log().IsLoggingAllEvents()) {
709 NetLog::TYPE_SPDY_SESSION_SEND_CREDENTIAL
,
711 new NetLogSpdyCredentialParameter(credential
.slot
,
714 return ERR_IO_PENDING
;
717 int SpdySession::WriteStreamData(SpdyStreamId stream_id
,
718 net::IOBuffer
* data
, int len
,
719 SpdyDataFlags flags
) {
721 DCHECK(IsStreamActive(stream_id
));
722 scoped_refptr
<SpdyStream
> stream
= active_streams_
[stream_id
];
723 CHECK_EQ(stream
->stream_id(), stream_id
);
725 return ERR_INVALID_SPDY_STREAM
;
727 if (len
> kMaxSpdyFrameChunkSize
) {
728 len
= kMaxSpdyFrameChunkSize
;
729 flags
= static_cast<SpdyDataFlags
>(flags
& ~DATA_FLAG_FIN
);
732 // Obey send window size of the stream if flow control is enabled.
734 if (stream
->send_window_size() <= 0) {
735 // Because we queue frames onto the session, it is possible that
736 // a stream was not flow controlled at the time it attempted the
737 // write, but when we go to fulfill the write, it is now flow
738 // controlled. This is why we need the session to mark the stream
739 // as stalled - because only the session knows for sure when the
741 stream
->set_stalled_by_flow_control(true);
743 NetLog::TYPE_SPDY_SESSION_STALLED_ON_SEND_WINDOW
,
745 new NetLogIntegerParameter("stream_id", stream_id
)));
746 return ERR_IO_PENDING
;
748 int new_len
= std::min(len
, stream
->send_window_size());
751 flags
= static_cast<SpdyDataFlags
>(flags
& ~DATA_FLAG_FIN
);
753 stream
->DecreaseSendWindowSize(len
);
756 if (net_log().IsLoggingAllEvents()) {
758 NetLog::TYPE_SPDY_SESSION_SEND_DATA
,
759 make_scoped_refptr(new NetLogSpdyDataParameter(stream_id
, len
, flags
)));
762 // Send PrefacePing for DATA_FRAMEs with nonzero payload size.
764 SendPrefacePingIfNoneInFlight();
766 // TODO(mbelshe): reduce memory copies here.
767 DCHECK(buffered_spdy_framer_
.get());
768 scoped_ptr
<SpdyDataFrame
> frame(
769 buffered_spdy_framer_
->CreateDataFrame(
770 stream_id
, data
->data(), len
, flags
));
771 QueueFrame(frame
.get(), stream
->priority(), stream
);
773 // Some servers don't like too many pings, so we limit our current sending to
774 // no more than two pings for any syn frame or data frame sent. To do this,
775 // we avoid ever setting this to true unless we send a syn (which we have just
776 // done) or data frame. This approach may change over time as servers change
777 // their responses to pings.
779 need_to_send_ping_
= true;
781 return ERR_IO_PENDING
;
784 void SpdySession::CloseStream(SpdyStreamId stream_id
, int status
) {
785 // TODO(mbelshe): We should send a RST_STREAM control frame here
786 // so that the server can cancel a large send.
788 DeleteStream(stream_id
, status
);
791 void SpdySession::ResetStream(SpdyStreamId stream_id
,
792 SpdyStatusCodes status
,
793 const std::string
& description
) {
796 NetLog::TYPE_SPDY_SESSION_SEND_RST_STREAM
,
797 make_scoped_refptr(new NetLogSpdyRstParameter(stream_id
, status
,
800 DCHECK(buffered_spdy_framer_
.get());
801 scoped_ptr
<SpdyRstStreamControlFrame
> rst_frame(
802 buffered_spdy_framer_
->CreateRstStream(stream_id
, status
));
804 // Default to lowest priority unless we know otherwise.
806 if(IsStreamActive(stream_id
)) {
807 scoped_refptr
<SpdyStream
> stream
= active_streams_
[stream_id
];
808 priority
= stream
->priority();
810 QueueFrame(rst_frame
.get(), priority
, NULL
);
811 DeleteStream(stream_id
, ERR_SPDY_PROTOCOL_ERROR
);
814 bool SpdySession::IsStreamActive(SpdyStreamId stream_id
) const {
815 return ContainsKey(active_streams_
, stream_id
);
818 LoadState
SpdySession::GetLoadState() const {
819 // NOTE: The application only queries the LoadState via the
820 // SpdyNetworkTransaction, and details are only needed when
821 // we're in the process of connecting.
823 // If we're connecting, defer to the connection to give us the actual
825 if (state_
== CONNECTING
)
826 return connection_
->GetLoadState();
828 // Just report that we're idle since the session could be doing
829 // many things concurrently.
830 return LOAD_STATE_IDLE
;
833 void SpdySession::OnReadComplete(int bytes_read
) {
834 // Parse a frame. For now this code requires that the frame fit into our
836 // TODO(mbelshe): support arbitrarily large frames!
838 read_pending_
= false;
840 if (bytes_read
<= 0) {
841 // Session is tearing down.
842 net::Error error
= static_cast<net::Error
>(bytes_read
);
844 error
= ERR_CONNECTION_CLOSED
;
845 CloseSessionOnError(error
, true, "bytes_read is <= 0.");
849 bytes_received_
+= bytes_read
;
851 received_data_time_
= base::TimeTicks::Now();
853 // The SpdyFramer will use callbacks onto |this| as it parses frames.
854 // When errors occur, those callbacks can lead to teardown of all references
855 // to |this|, so maintain a reference to self during this call for safe
857 scoped_refptr
<SpdySession
> self(this);
859 DCHECK(buffered_spdy_framer_
.get());
860 char *data
= read_buffer_
->data();
862 buffered_spdy_framer_
->error_code() ==
863 SpdyFramer::SPDY_NO_ERROR
) {
864 uint32 bytes_processed
=
865 buffered_spdy_framer_
->ProcessInput(data
, bytes_read
);
866 bytes_read
-= bytes_processed
;
867 data
+= bytes_processed
;
868 if (buffered_spdy_framer_
->state() == SpdyFramer::SPDY_DONE
)
869 buffered_spdy_framer_
->Reset();
872 if (state_
!= CLOSED
)
876 void SpdySession::OnWriteComplete(int result
) {
877 DCHECK(write_pending_
);
878 DCHECK(in_flight_write_
.size());
880 write_pending_
= false;
882 scoped_refptr
<SpdyStream
> stream
= in_flight_write_
.stream();
885 // It should not be possible to have written more bytes than our
887 DCHECK_LE(result
, in_flight_write_
.buffer()->BytesRemaining());
889 in_flight_write_
.buffer()->DidConsume(result
);
891 // We only notify the stream when we've fully written the pending frame.
892 if (!in_flight_write_
.buffer()->BytesRemaining()) {
894 // Report the number of bytes written to the caller, but exclude the
895 // frame size overhead. NOTE: if this frame was compressed the
896 // reported bytes written is the compressed size, not the original
899 result
= in_flight_write_
.buffer()->size();
900 DCHECK_GE(result
, static_cast<int>(SpdyFrame::kHeaderSize
));
901 result
-= static_cast<int>(SpdyFrame::kHeaderSize
);
904 // It is possible that the stream was cancelled while we were writing
906 if (!stream
->cancelled())
907 stream
->OnWriteComplete(result
);
910 // Cleanup the write which just completed.
911 in_flight_write_
.release();
914 // Write more data. We're already in a continuation, so we can
915 // go ahead and write it immediately (without going back to the
919 in_flight_write_
.release();
921 // The stream is now errored. Close it down.
923 static_cast<net::Error
>(result
), true, "The stream has errored.");
927 net::Error
SpdySession::ReadSocket() {
931 if (state_
== CLOSED
) {
933 return ERR_UNEXPECTED
;
936 CHECK(connection_
.get());
937 CHECK(connection_
->socket());
938 int bytes_read
= connection_
->socket()->Read(
941 base::Bind(&SpdySession::OnReadComplete
, base::Unretained(this)));
942 switch (bytes_read
) {
945 CloseSessionOnError(ERR_CONNECTION_CLOSED
, true, "bytes_read is 0.");
946 return ERR_CONNECTION_CLOSED
;
947 case net::ERR_IO_PENDING
:
948 // Waiting for data. Nothing to do now.
949 read_pending_
= true;
950 return ERR_IO_PENDING
;
952 // Data was read, process it.
953 // Schedule the work through the message loop to avoid recursive
955 read_pending_
= true;
956 MessageLoop::current()->PostTask(
958 base::Bind(&SpdySession::OnReadComplete
,
959 weak_factory_
.GetWeakPtr(), bytes_read
));
965 void SpdySession::WriteSocketLater() {
966 if (delayed_write_pending_
)
969 if (state_
< CONNECTED
)
972 delayed_write_pending_
= true;
973 MessageLoop::current()->PostTask(
975 base::Bind(&SpdySession::WriteSocket
, weak_factory_
.GetWeakPtr()));
978 void SpdySession::WriteSocket() {
979 // This function should only be called via WriteSocketLater.
980 DCHECK(delayed_write_pending_
);
981 delayed_write_pending_
= false;
983 // If the socket isn't connected yet, just wait; we'll get called
984 // again when the socket connection completes. If the socket is
985 // closed, just return.
986 if (state_
< CONNECTED
|| state_
== CLOSED
)
989 if (write_pending_
) // Another write is in progress still.
992 // Loop sending frames until we've sent everything or until the write
993 // returns error (or ERR_IO_PENDING).
994 DCHECK(buffered_spdy_framer_
.get());
995 while (in_flight_write_
.buffer() || !queue_
.empty()) {
996 if (!in_flight_write_
.buffer()) {
997 // Grab the next SpdyFrame to send.
998 SpdyIOBuffer next_buffer
= queue_
.top();
1001 // We've deferred compression until just before we write it to the socket,
1002 // which is now. At this time, we don't compress our data frames.
1003 SpdyFrame
uncompressed_frame(next_buffer
.buffer()->data(), false);
1005 if (buffered_spdy_framer_
->IsCompressible(uncompressed_frame
)) {
1006 scoped_ptr
<SpdyFrame
> compressed_frame(
1007 buffered_spdy_framer_
->CompressFrame(uncompressed_frame
));
1008 if (!compressed_frame
.get()) {
1009 CloseSessionOnError(
1010 net::ERR_SPDY_PROTOCOL_ERROR
, true, "SPDY Compression failure.");
1014 size
= compressed_frame
->length() + SpdyFrame::kHeaderSize
;
1016 DCHECK_GT(size
, 0u);
1018 // TODO(mbelshe): We have too much copying of data here.
1019 IOBufferWithSize
* buffer
= new IOBufferWithSize(size
);
1020 memcpy(buffer
->data(), compressed_frame
->data(), size
);
1022 // Attempt to send the frame.
1023 in_flight_write_
= SpdyIOBuffer(buffer
, size
, 0, next_buffer
.stream());
1025 size
= uncompressed_frame
.length() + SpdyFrame::kHeaderSize
;
1026 in_flight_write_
= next_buffer
;
1029 DCHECK(in_flight_write_
.buffer()->BytesRemaining());
1032 write_pending_
= true;
1033 int rv
= connection_
->socket()->Write(
1034 in_flight_write_
.buffer(),
1035 in_flight_write_
.buffer()->BytesRemaining(),
1036 base::Bind(&SpdySession::OnWriteComplete
, base::Unretained(this)));
1037 if (rv
== net::ERR_IO_PENDING
)
1040 // We sent the frame successfully.
1041 OnWriteComplete(rv
);
1043 // TODO(mbelshe): Test this error case. Maybe we should mark the socket
1044 // as in an error state.
1050 void SpdySession::CloseAllStreams(net::Error status
) {
1051 base::StatsCounter
abandoned_streams("spdy.abandoned_streams");
1052 base::StatsCounter
abandoned_push_streams(
1053 "spdy.abandoned_push_streams");
1055 if (!active_streams_
.empty())
1056 abandoned_streams
.Add(active_streams_
.size());
1057 if (!unclaimed_pushed_streams_
.empty()) {
1058 streams_abandoned_count_
+= unclaimed_pushed_streams_
.size();
1059 abandoned_push_streams
.Add(unclaimed_pushed_streams_
.size());
1060 unclaimed_pushed_streams_
.clear();
1063 for (int i
= 0;i
< NUM_PRIORITIES
;++i
) {
1064 while (!create_stream_queues_
[i
].empty()) {
1065 PendingCreateStream pending_create
= create_stream_queues_
[i
].front();
1066 create_stream_queues_
[i
].pop();
1067 pending_create
.callback
.Run(ERR_ABORTED
);
1071 while (!active_streams_
.empty()) {
1072 ActiveStreamMap::iterator it
= active_streams_
.begin();
1073 const scoped_refptr
<SpdyStream
>& stream
= it
->second
;
1075 std::string description
= base::StringPrintf(
1076 "ABANDONED (stream_id=%d): ", stream
->stream_id()) + stream
->path();
1077 stream
->LogStreamError(status
, description
);
1078 DeleteStream(stream
->stream_id(), status
);
1081 // We also need to drain the queue.
1082 while (queue_
.size())
1086 int SpdySession::GetNewStreamId() {
1087 int id
= stream_hi_water_mark_
;
1088 stream_hi_water_mark_
+= 2;
1089 if (stream_hi_water_mark_
> 0x7fff)
1090 stream_hi_water_mark_
= 1;
1094 void SpdySession::QueueFrame(SpdyFrame
* frame
,
1095 SpdyPriority priority
,
1096 SpdyStream
* stream
) {
1097 int length
= SpdyFrame::kHeaderSize
+ frame
->length();
1098 IOBuffer
* buffer
= new IOBuffer(length
);
1099 memcpy(buffer
->data(), frame
->data(), length
);
1100 queue_
.push(SpdyIOBuffer(buffer
, length
, priority
, stream
));
1105 void SpdySession::CloseSessionOnError(net::Error err
,
1106 bool remove_from_pool
,
1107 const std::string
& description
) {
1108 // Closing all streams can have a side-effect of dropping the last reference
1109 // to |this|. Hold a reference through this function.
1110 scoped_refptr
<SpdySession
> self(this);
1114 NetLog::TYPE_SPDY_SESSION_CLOSE
,
1116 new NetLogSpdySessionCloseParameter(err
, description
)));
1118 // Don't close twice. This can occur because we can have both
1119 // a read and a write outstanding, and each can complete with
1121 if (state_
!= CLOSED
) {
1124 if (remove_from_pool
)
1126 CloseAllStreams(err
);
1130 Value
* SpdySession::GetInfoAsValue() const {
1131 DictionaryValue
* dict
= new DictionaryValue();
1133 dict
->SetInteger("source_id", net_log_
.source().id
);
1135 dict
->SetString("host_port_pair", host_port_proxy_pair_
.first
.ToString());
1136 if (!pooled_aliases_
.empty()) {
1137 ListValue
* alias_list
= new ListValue();
1138 for (std::set
<HostPortProxyPair
>::const_iterator it
=
1139 pooled_aliases_
.begin();
1140 it
!= pooled_aliases_
.end(); it
++) {
1141 alias_list
->Append(Value::CreateStringValue(it
->first
.ToString()));
1143 dict
->Set("aliases", alias_list
);
1145 dict
->SetString("proxy", host_port_proxy_pair_
.second
.ToURI());
1147 dict
->SetInteger("active_streams", active_streams_
.size());
1149 dict
->SetInteger("unclaimed_pushed_streams",
1150 unclaimed_pushed_streams_
.size());
1152 dict
->SetBoolean("is_secure", is_secure_
);
1154 SSLClientSocket::NextProto proto
= SSLClientSocket::kProtoUnknown
;
1156 proto
= GetSSLClientSocket()->protocol_negotiated();
1158 dict
->SetString("protocol_negotiated",
1159 SSLClientSocket::NextProtoToString(proto
));
1161 dict
->SetInteger("error", error_
);
1162 dict
->SetInteger("max_concurrent_streams", max_concurrent_streams_
);
1164 dict
->SetInteger("streams_initiated_count", streams_initiated_count_
);
1165 dict
->SetInteger("streams_pushed_count", streams_pushed_count_
);
1166 dict
->SetInteger("streams_pushed_and_claimed_count",
1167 streams_pushed_and_claimed_count_
);
1168 dict
->SetInteger("streams_abandoned_count", streams_abandoned_count_
);
1169 DCHECK(buffered_spdy_framer_
.get());
1170 dict
->SetInteger("frames_received", buffered_spdy_framer_
->frames_received());
1172 dict
->SetBoolean("sent_settings", sent_settings_
);
1173 dict
->SetBoolean("received_settings", received_settings_
);
1177 bool SpdySession::IsReused() const {
1178 return buffered_spdy_framer_
->frames_received() > 0;
1181 int SpdySession::GetPeerAddress(AddressList
* address
) const {
1182 if (!connection_
->socket())
1183 return ERR_SOCKET_NOT_CONNECTED
;
1185 return connection_
->socket()->GetPeerAddress(address
);
1188 int SpdySession::GetLocalAddress(IPEndPoint
* address
) const {
1189 if (!connection_
->socket())
1190 return ERR_SOCKET_NOT_CONNECTED
;
1192 return connection_
->socket()->GetLocalAddress(address
);
1195 bool SpdySession::CloseOneIdleConnection() {
1196 if (spdy_session_pool_
&& num_active_streams() == 0) {
1197 bool ret
= HasOneRef();
1198 // Will remove a reference to this.
1200 // Since the underlying socket is only returned when |this| is destroyed
1201 // we should only return true if RemoveFromPool() removed the last ref.
1207 void SpdySession::ActivateStream(SpdyStream
* stream
) {
1208 const SpdyStreamId id
= stream
->stream_id();
1209 DCHECK(!IsStreamActive(id
));
1211 active_streams_
[id
] = stream
;
1214 void SpdySession::DeleteStream(SpdyStreamId id
, int status
) {
1215 // For push streams, if they are being deleted normally, we leave
1216 // the stream in the unclaimed_pushed_streams_ list. However, if
1217 // the stream is errored out, clean it up entirely.
1219 PushedStreamMap::iterator it
;
1220 for (it
= unclaimed_pushed_streams_
.begin();
1221 it
!= unclaimed_pushed_streams_
.end(); ++it
) {
1222 scoped_refptr
<SpdyStream
> curr
= it
->second
;
1223 if (id
== curr
->stream_id()) {
1224 unclaimed_pushed_streams_
.erase(it
);
1230 // The stream might have been deleted.
1231 ActiveStreamMap::iterator it2
= active_streams_
.find(id
);
1232 if (it2
== active_streams_
.end())
1235 // If this is an active stream, call the callback.
1236 const scoped_refptr
<SpdyStream
> stream(it2
->second
);
1237 active_streams_
.erase(it2
);
1239 stream
->OnClose(status
);
1240 ProcessPendingCreateStreams();
1243 void SpdySession::RemoveFromPool() {
1244 if (spdy_session_pool_
) {
1245 SpdySessionPool
* pool
= spdy_session_pool_
;
1246 spdy_session_pool_
= NULL
;
1247 pool
->Remove(make_scoped_refptr(this));
1251 scoped_refptr
<SpdyStream
> SpdySession::GetActivePushStream(
1252 const std::string
& path
) {
1253 base::StatsCounter
used_push_streams("spdy.claimed_push_streams");
1255 PushedStreamMap::iterator it
= unclaimed_pushed_streams_
.find(path
);
1256 if (it
!= unclaimed_pushed_streams_
.end()) {
1257 net_log_
.AddEvent(NetLog::TYPE_SPDY_STREAM_ADOPTED_PUSH_STREAM
, NULL
);
1258 scoped_refptr
<SpdyStream
> stream
= it
->second
;
1259 unclaimed_pushed_streams_
.erase(it
);
1260 used_push_streams
.Increment();
1266 bool SpdySession::GetSSLInfo(SSLInfo
* ssl_info
,
1267 bool* was_npn_negotiated
,
1268 SSLClientSocket::NextProto
* protocol_negotiated
) {
1270 *protocol_negotiated
= SSLClientSocket::kProtoUnknown
;
1273 SSLClientSocket
* ssl_socket
= GetSSLClientSocket();
1274 ssl_socket
->GetSSLInfo(ssl_info
);
1275 *was_npn_negotiated
= ssl_socket
->was_npn_negotiated();
1276 *protocol_negotiated
= ssl_socket
->protocol_negotiated();
1280 bool SpdySession::GetSSLCertRequestInfo(
1281 SSLCertRequestInfo
* cert_request_info
) {
1284 GetSSLClientSocket()->GetSSLCertRequestInfo(cert_request_info
);
1288 ServerBoundCertService
* SpdySession::GetServerBoundCertService() const {
1291 return GetSSLClientSocket()->GetServerBoundCertService();
1294 SSLClientCertType
SpdySession::GetDomainBoundCertType() const {
1296 return CLIENT_CERT_INVALID_TYPE
;
1297 return GetSSLClientSocket()->domain_bound_cert_type();
1300 void SpdySession::OnError(int error_code
) {
1301 std::string description
= base::StringPrintf(
1302 "SPDY_ERROR error_code: %d.", error_code
);
1303 CloseSessionOnError(net::ERR_SPDY_PROTOCOL_ERROR
, true, description
);
1306 void SpdySession::OnStreamError(SpdyStreamId stream_id
,
1307 const std::string
& description
) {
1308 if (IsStreamActive(stream_id
))
1309 ResetStream(stream_id
, PROTOCOL_ERROR
, description
);
1312 void SpdySession::OnStreamFrameData(SpdyStreamId stream_id
,
1315 if (net_log().IsLoggingAllEvents()) {
1317 NetLog::TYPE_SPDY_SESSION_RECV_DATA
,
1318 make_scoped_refptr(new NetLogSpdyDataParameter(
1319 stream_id
, len
, SpdyDataFlags())));
1322 if (!IsStreamActive(stream_id
)) {
1323 // NOTE: it may just be that the stream was cancelled.
1327 scoped_refptr
<SpdyStream
> stream
= active_streams_
[stream_id
];
1328 stream
->OnDataReceived(data
, len
);
1331 void SpdySession::OnSetting(SpdySettingsIds id
,
1334 HandleSetting(id
, value
);
1335 SettingsFlagsAndId
flags_and_id(flags
, id
);
1336 // TODO(rtenneti): persist SpdySetting.
1337 // http_server_properties_->SetSpdySetting(
1338 // host_port_pair(), std::make_pair(flags_and_id, value));
1340 received_settings_
= true;
1342 // Log the settings.
1343 SpdySettings settings
;
1344 settings
.insert(settings
.end(), std::make_pair(flags_and_id
, value
));
1346 NetLog::TYPE_SPDY_SESSION_RECV_SETTINGS
,
1347 make_scoped_refptr(new NetLogSpdySettingsParameter(settings
)));
1350 bool SpdySession::Respond(const SpdyHeaderBlock
& headers
,
1351 const scoped_refptr
<SpdyStream
> stream
) {
1353 rv
= stream
->OnResponseReceived(headers
);
1355 DCHECK_NE(rv
, ERR_IO_PENDING
);
1356 const SpdyStreamId stream_id
= stream
->stream_id();
1357 DeleteStream(stream_id
, rv
);
1363 void SpdySession::OnSynStream(
1364 const SpdySynStreamControlFrame
& frame
,
1365 const linked_ptr
<SpdyHeaderBlock
>& headers
) {
1366 SpdyStreamId stream_id
= frame
.stream_id();
1367 SpdyStreamId associated_stream_id
= frame
.associated_stream_id();
1369 if (net_log_
.IsLoggingAllEvents()) {
1371 NetLog::TYPE_SPDY_SESSION_PUSHED_SYN_STREAM
,
1372 make_scoped_refptr(new NetLogSpdySynParameter(
1373 headers
, static_cast<SpdyControlFlags
>(frame
.flags()),
1374 stream_id
, associated_stream_id
)));
1377 // Server-initiated streams should have even sequence numbers.
1378 if ((stream_id
& 0x1) != 0) {
1379 LOG(WARNING
) << "Received invalid OnSyn stream id " << stream_id
;
1383 if (IsStreamActive(stream_id
)) {
1384 LOG(WARNING
) << "Received OnSyn for active stream " << stream_id
;
1388 if (associated_stream_id
== 0) {
1389 std::string description
= base::StringPrintf(
1390 "Received invalid OnSyn associated stream id %d for stream %d",
1391 associated_stream_id
, stream_id
);
1392 ResetStream(stream_id
, INVALID_STREAM
, description
);
1396 streams_pushed_count_
++;
1398 // TODO(mbelshe): DCHECK that this is a GET method?
1400 // Verify that the response had a URL for us.
1401 const std::string
& url
= ContainsKey(*headers
, "url") ?
1402 headers
->find("url")->second
: "";
1404 ResetStream(stream_id
, PROTOCOL_ERROR
,
1405 "Pushed stream did not contain a url.");
1410 if (!gurl
.is_valid()) {
1411 ResetStream(stream_id
, PROTOCOL_ERROR
,
1412 "Pushed stream url was invalid: " + url
);
1416 // Verify we have a valid stream association.
1417 if (!IsStreamActive(associated_stream_id
)) {
1418 ResetStream(stream_id
, INVALID_ASSOCIATED_STREAM
,
1420 "Received OnSyn with inactive associated stream %d",
1421 associated_stream_id
));
1425 scoped_refptr
<SpdyStream
> associated_stream
=
1426 active_streams_
[associated_stream_id
];
1427 GURL
associated_url(associated_stream
->GetUrl());
1428 if (associated_url
.GetOrigin() != gurl
.GetOrigin()) {
1429 ResetStream(stream_id
, REFUSED_STREAM
,
1431 "Rejected Cross Origin Push Stream %d",
1432 associated_stream_id
));
1436 // There should not be an existing pushed stream with the same path.
1437 PushedStreamMap::iterator it
= unclaimed_pushed_streams_
.find(url
);
1438 if (it
!= unclaimed_pushed_streams_
.end()) {
1439 ResetStream(stream_id
, PROTOCOL_ERROR
,
1440 "Received duplicate pushed stream with url: " + url
);
1444 scoped_refptr
<SpdyStream
> stream(
1445 new SpdyStream(this, stream_id
, true, net_log_
));
1447 stream
->set_path(gurl
.PathForRequest());
1448 stream
->set_send_window_size(initial_send_window_size_
);
1449 stream
->set_recv_window_size(initial_recv_window_size_
);
1451 unclaimed_pushed_streams_
[url
] = stream
;
1453 ActivateStream(stream
);
1454 stream
->set_response_received();
1456 // Parse the headers.
1457 if (!Respond(*headers
, stream
))
1460 base::StatsCounter
push_requests("spdy.pushed_streams");
1461 push_requests
.Increment();
1464 void SpdySession::OnSynReply(const SpdySynReplyControlFrame
& frame
,
1465 const linked_ptr
<SpdyHeaderBlock
>& headers
) {
1466 SpdyStreamId stream_id
= frame
.stream_id();
1468 if (net_log().IsLoggingAllEvents()) {
1470 NetLog::TYPE_SPDY_SESSION_SYN_REPLY
,
1471 make_scoped_refptr(new NetLogSpdySynParameter(
1472 headers
, static_cast<SpdyControlFlags
>(frame
.flags()),
1476 bool valid_stream
= IsStreamActive(stream_id
);
1477 if (!valid_stream
) {
1478 // NOTE: it may just be that the stream was cancelled.
1479 LOG(WARNING
) << "Received SYN_REPLY for invalid stream " << stream_id
;
1483 scoped_refptr
<SpdyStream
> stream
= active_streams_
[stream_id
];
1484 CHECK_EQ(stream
->stream_id(), stream_id
);
1485 CHECK(!stream
->cancelled());
1487 if (stream
->response_received()) {
1488 stream
->LogStreamError(ERR_SYN_REPLY_NOT_RECEIVED
,
1489 "Received duplicate SYN_REPLY for stream.");
1490 CloseStream(stream
->stream_id(), ERR_SPDY_PROTOCOL_ERROR
);
1493 stream
->set_response_received();
1495 Respond(*headers
, stream
);
1498 void SpdySession::OnHeaders(const SpdyHeadersControlFrame
& frame
,
1499 const linked_ptr
<SpdyHeaderBlock
>& headers
) {
1500 SpdyStreamId stream_id
= frame
.stream_id();
1502 if (net_log().IsLoggingAllEvents()) {
1504 NetLog::TYPE_SPDY_SESSION_HEADERS
,
1505 make_scoped_refptr(new NetLogSpdySynParameter(
1506 headers
, static_cast<SpdyControlFlags
>(frame
.flags()),
1510 bool valid_stream
= IsStreamActive(stream_id
);
1511 if (!valid_stream
) {
1512 // NOTE: it may just be that the stream was cancelled.
1513 LOG(WARNING
) << "Received HEADERS for invalid stream " << stream_id
;
1517 scoped_refptr
<SpdyStream
> stream
= active_streams_
[stream_id
];
1518 CHECK_EQ(stream
->stream_id(), stream_id
);
1519 CHECK(!stream
->cancelled());
1521 int rv
= stream
->OnHeaders(*headers
);
1523 DCHECK_NE(rv
, ERR_IO_PENDING
);
1524 const SpdyStreamId stream_id
= stream
->stream_id();
1525 DeleteStream(stream_id
, rv
);
1529 void SpdySession::OnRstStream(const SpdyRstStreamControlFrame
& frame
) {
1530 SpdyStreamId stream_id
= frame
.stream_id();
1533 NetLog::TYPE_SPDY_SESSION_RST_STREAM
,
1535 new NetLogSpdyRstParameter(stream_id
, frame
.status(), "")));
1537 bool valid_stream
= IsStreamActive(stream_id
);
1538 if (!valid_stream
) {
1539 // NOTE: it may just be that the stream was cancelled.
1540 LOG(WARNING
) << "Received RST for invalid stream" << stream_id
;
1543 scoped_refptr
<SpdyStream
> stream
= active_streams_
[stream_id
];
1544 CHECK_EQ(stream
->stream_id(), stream_id
);
1545 CHECK(!stream
->cancelled());
1547 if (frame
.status() == 0) {
1548 stream
->OnDataReceived(NULL
, 0);
1549 } else if (frame
.status() == REFUSED_STREAM
) {
1550 DeleteStream(stream_id
, ERR_SPDY_SERVER_REFUSED_STREAM
);
1552 stream
->LogStreamError(ERR_SPDY_PROTOCOL_ERROR
,
1553 base::StringPrintf("SPDY stream closed: %d",
1555 // TODO(mbelshe): Map from Spdy-protocol errors to something sensical.
1556 // For now, it doesn't matter much - it is a protocol error.
1557 DeleteStream(stream_id
, ERR_SPDY_PROTOCOL_ERROR
);
1561 void SpdySession::OnGoAway(const SpdyGoAwayControlFrame
& frame
) {
1563 NetLog::TYPE_SPDY_SESSION_GOAWAY
,
1565 new NetLogSpdyGoAwayParameter(frame
.last_accepted_stream_id(),
1566 active_streams_
.size(),
1567 unclaimed_pushed_streams_
.size())));
1569 CloseAllStreams(net::ERR_ABORTED
);
1571 // TODO(willchan): Cancel any streams that are past the GoAway frame's
1572 // |last_accepted_stream_id|.
1574 // Don't bother killing any streams that are still reading. They'll either
1575 // complete successfully or get an ERR_CONNECTION_CLOSED when the socket is
1579 void SpdySession::OnPing(const SpdyPingControlFrame
& frame
) {
1581 NetLog::TYPE_SPDY_SESSION_PING
,
1583 new NetLogSpdyPingParameter(frame
.unique_id(), "received")));
1585 // Send response to a PING from server.
1586 if (frame
.unique_id() % 2 == 0) {
1587 WritePingFrame(frame
.unique_id());
1592 if (pings_in_flight_
< 0) {
1593 CloseSessionOnError(
1594 net::ERR_SPDY_PROTOCOL_ERROR
, true, "pings_in_flight_ is < 0.");
1598 if (pings_in_flight_
> 0)
1601 // We will record RTT in histogram when there are no more client sent
1602 // pings_in_flight_.
1603 RecordPingRTTHistogram(base::TimeTicks::Now() - last_ping_sent_time_
);
1605 if (!need_to_send_ping_
)
1608 PlanToSendTrailingPing();
1611 void SpdySession::OnWindowUpdate(
1612 const SpdyWindowUpdateControlFrame
& frame
) {
1613 SpdyStreamId stream_id
= frame
.stream_id();
1614 int32 delta_window_size
= static_cast<int32
>(frame
.delta_window_size());
1616 NetLog::TYPE_SPDY_SESSION_RECEIVED_WINDOW_UPDATE
,
1617 make_scoped_refptr(new NetLogSpdyWindowUpdateParameter(
1618 stream_id
, delta_window_size
)));
1620 if (!IsStreamActive(stream_id
)) {
1621 LOG(WARNING
) << "Received WINDOW_UPDATE for invalid stream " << stream_id
;
1625 if (delta_window_size
< 1) {
1626 ResetStream(stream_id
, FLOW_CONTROL_ERROR
,
1628 "Received WINDOW_UPDATE with an invalid "
1629 "delta_window_size %d", delta_window_size
));
1633 scoped_refptr
<SpdyStream
> stream
= active_streams_
[stream_id
];
1634 CHECK_EQ(stream
->stream_id(), stream_id
);
1635 CHECK(!stream
->cancelled());
1638 stream
->IncreaseSendWindowSize(delta_window_size
);
1641 void SpdySession::SendWindowUpdate(SpdyStreamId stream_id
,
1642 int32 delta_window_size
) {
1643 DCHECK(IsStreamActive(stream_id
));
1644 scoped_refptr
<SpdyStream
> stream
= active_streams_
[stream_id
];
1645 CHECK_EQ(stream
->stream_id(), stream_id
);
1648 NetLog::TYPE_SPDY_SESSION_SENT_WINDOW_UPDATE
,
1649 make_scoped_refptr(new NetLogSpdyWindowUpdateParameter(
1650 stream_id
, delta_window_size
)));
1652 DCHECK(buffered_spdy_framer_
.get());
1653 scoped_ptr
<SpdyWindowUpdateControlFrame
> window_update_frame(
1654 buffered_spdy_framer_
->CreateWindowUpdate(stream_id
, delta_window_size
));
1655 QueueFrame(window_update_frame
.get(), stream
->priority(), NULL
);
1658 // Given a cwnd that we would have sent to the server, modify it based on the
1659 // field trial policy.
1660 uint32
ApplyCwndFieldTrialPolicy(int cwnd
) {
1661 base::FieldTrial
* trial
= base::FieldTrialList::Find("SpdyCwnd");
1663 LOG(WARNING
) << "Could not find \"SpdyCwnd\" in FieldTrialList";
1666 if (trial
->group_name() == "cwnd10")
1668 else if (trial
->group_name() == "cwnd16")
1670 else if (trial
->group_name() == "cwndMin16")
1671 return std::max(cwnd
, 16);
1672 else if (trial
->group_name() == "cwndMin10")
1673 return std::max(cwnd
, 10);
1674 else if (trial
->group_name() == "cwndDynamic")
1680 void SpdySession::SendSettings() {
1681 // Note: we're copying the settings here, so that we can potentially modify
1682 // the settings for the field trial. When removing the field trial, make
1683 // this a reference to the const SpdySettings again.
1684 SpdySettings settings
=
1685 http_server_properties_
->GetSpdySettings(host_port_pair());
1686 if (settings
.empty())
1689 typedef std::map
<uint32
, SpdySetting
> SpdySettingsMap
;
1690 SpdySettingsMap unique_settings
;
1692 // Record Histogram Data and Apply the SpdyCwnd FieldTrial if applicable.
1693 for (SpdySettings::iterator i
= settings
.begin(),
1694 end
= settings
.end(); i
!= end
; ++i
) {
1695 const uint32 id
= i
->first
.id();
1696 const uint32 val
= i
->second
;
1698 case SETTINGS_CURRENT_CWND
:
1700 cwnd
= ApplyCwndFieldTrialPolicy(val
);
1701 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdySettingsCwndSent",
1705 SettingsFlagsAndId
new_id(SETTINGS_FLAG_PLEASE_PERSIST
,
1709 SpdySetting
setting(new_id
, val
);
1710 // TODO(rtenneti): Persist SpdySetting.
1711 // http_server_properties_->SetSpdySetting(host_port_pair(), setting);
1712 unique_settings
[id
] = setting
;
1716 unique_settings
[id
] = *i
;
1719 HandleSettings(settings
);
1722 NetLog::TYPE_SPDY_SESSION_SEND_SETTINGS
,
1723 make_scoped_refptr(new NetLogSpdySettingsParameter(settings
)));
1725 SpdySettings sorted_settings
;
1726 for (SpdySettingsMap::iterator it
= unique_settings
.begin();
1727 unique_settings
.end() != it
;
1729 sorted_settings
.push_back(it
->second
);
1732 // Create the SETTINGS frame and send it.
1733 DCHECK(buffered_spdy_framer_
.get());
1734 scoped_ptr
<SpdySettingsControlFrame
> settings_frame(
1735 buffered_spdy_framer_
->CreateSettings(sorted_settings
));
1736 sent_settings_
= true;
1737 QueueFrame(settings_frame
.get(), 0, NULL
);
1740 void SpdySession::HandleSettings(const SpdySettings
& settings
) {
1741 for (SpdySettings::const_iterator i
= settings
.begin(),
1742 end
= settings
.end(); i
!= end
; ++i
) {
1743 HandleSetting(i
->first
.id(), i
->second
);
1747 void SpdySession::HandleSetting(uint32 id
, uint32 value
) {
1749 case SETTINGS_MAX_CONCURRENT_STREAMS
:
1750 max_concurrent_streams_
= std::min(static_cast<size_t>(value
),
1751 g_max_concurrent_stream_limit
);
1752 ProcessPendingCreateStreams();
1754 case SETTINGS_INITIAL_WINDOW_SIZE
:
1755 // INITIAL_WINDOW_SIZE updates initial_send_window_size_ only.
1756 // TODO(rtenneti): discuss with the server team about
1757 // initial_recv_window_size_.
1758 int32 prev_initial_send_window_size
= initial_send_window_size_
;
1759 initial_send_window_size_
= value
;
1760 int32 delta_window_size
=
1761 initial_send_window_size_
- prev_initial_send_window_size
;
1762 UpdateStreamsSendWindowSize(delta_window_size
);
1767 void SpdySession::UpdateStreamsSendWindowSize(int32 delta_window_size
) {
1768 ActiveStreamMap::iterator it
;
1769 for (it
= active_streams_
.begin(); it
!= active_streams_
.end(); ++it
) {
1770 const scoped_refptr
<SpdyStream
>& stream
= it
->second
;
1772 stream
->AdjustSendWindowSize(delta_window_size
);
1776 void SpdySession::SendPrefacePingIfNoneInFlight() {
1777 if (pings_in_flight_
|| trailing_ping_pending_
||
1778 !g_enable_ping_based_connection_checking
)
1781 base::TimeTicks now
= base::TimeTicks::Now();
1782 // If we haven't heard from server, then send a preface-PING.
1783 if ((now
- received_data_time_
) > connection_at_risk_of_loss_time_
)
1786 PlanToSendTrailingPing();
1789 void SpdySession::SendPrefacePing() {
1790 WritePingFrame(next_ping_id_
);
1793 void SpdySession::PlanToSendTrailingPing() {
1794 if (trailing_ping_pending_
)
1797 trailing_ping_pending_
= true;
1798 MessageLoop::current()->PostDelayedTask(
1800 base::Bind(&SpdySession::SendTrailingPing
, weak_factory_
.GetWeakPtr()),
1801 trailing_ping_delay_time_
);
1804 void SpdySession::SendTrailingPing() {
1805 DCHECK(trailing_ping_pending_
);
1806 trailing_ping_pending_
= false;
1807 WritePingFrame(next_ping_id_
);
1810 void SpdySession::WritePingFrame(uint32 unique_id
) {
1811 DCHECK(buffered_spdy_framer_
.get());
1812 scoped_ptr
<SpdyPingControlFrame
> ping_frame(
1813 buffered_spdy_framer_
->CreatePingFrame(next_ping_id_
));
1815 ping_frame
.get(), buffered_spdy_framer_
->GetHighestPriority(), NULL
);
1817 if (net_log().IsLoggingAllEvents()) {
1819 NetLog::TYPE_SPDY_SESSION_PING
,
1820 make_scoped_refptr(new NetLogSpdyPingParameter(next_ping_id_
, "sent")));
1822 if (unique_id
% 2 != 0) {
1825 need_to_send_ping_
= false;
1826 PlanToCheckPingStatus();
1827 last_ping_sent_time_
= base::TimeTicks::Now();
1831 void SpdySession::PlanToCheckPingStatus() {
1832 if (check_ping_status_pending_
)
1835 check_ping_status_pending_
= true;
1836 MessageLoop::current()->PostDelayedTask(
1838 base::Bind(&SpdySession::CheckPingStatus
, weak_factory_
.GetWeakPtr(),
1839 base::TimeTicks::Now()), hung_interval_
);
1842 void SpdySession::CheckPingStatus(base::TimeTicks last_check_time
) {
1843 // Check if we got a response back for all PINGs we had sent.
1844 if (pings_in_flight_
== 0) {
1845 check_ping_status_pending_
= false;
1849 DCHECK(check_ping_status_pending_
);
1851 base::TimeTicks now
= base::TimeTicks::Now();
1852 base::TimeDelta delay
= hung_interval_
- (now
- received_data_time_
);
1854 if (delay
.InMilliseconds() < 0 || received_data_time_
< last_check_time
) {
1855 CloseSessionOnError(net::ERR_SPDY_PING_FAILED
, true, "Failed ping.");
1856 // Track all failed PING messages in a separate bucket.
1857 const base::TimeDelta kFailedPing
=
1858 base::TimeDelta::FromInternalValue(INT_MAX
);
1859 RecordPingRTTHistogram(kFailedPing
);
1863 // Check the status of connection after a delay.
1864 MessageLoop::current()->PostDelayedTask(
1866 base::Bind(&SpdySession::CheckPingStatus
, weak_factory_
.GetWeakPtr(),
1871 void SpdySession::RecordPingRTTHistogram(base::TimeDelta duration
) {
1872 UMA_HISTOGRAM_TIMES("Net.SpdyPing.RTT", duration
);
1875 void SpdySession::RecordHistograms() {
1876 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsPerSession",
1877 streams_initiated_count_
,
1879 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsPushedPerSession",
1880 streams_pushed_count_
,
1882 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsPushedAndClaimedPerSession",
1883 streams_pushed_and_claimed_count_
,
1885 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsAbandonedPerSession",
1886 streams_abandoned_count_
,
1888 UMA_HISTOGRAM_ENUMERATION("Net.SpdySettingsSent",
1889 sent_settings_
? 1 : 0, 2);
1890 UMA_HISTOGRAM_ENUMERATION("Net.SpdySettingsReceived",
1891 received_settings_
? 1 : 0, 2);
1892 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamStallsPerSession",
1895 UMA_HISTOGRAM_ENUMERATION("Net.SpdySessionsWithStalls",
1896 stalled_streams_
> 0 ? 1 : 0, 2);
1898 if (received_settings_
) {
1899 // Enumerate the saved settings, and set histograms for it.
1900 const SpdySettings
& settings
=
1901 http_server_properties_
->GetSpdySettings(host_port_pair());
1903 SpdySettings::const_iterator it
;
1904 for (it
= settings
.begin(); it
!= settings
.end(); ++it
) {
1905 const SpdySetting setting
= *it
;
1906 switch (setting
.first
.id()) {
1907 case SETTINGS_CURRENT_CWND
:
1908 // Record several different histograms to see if cwnd converges
1909 // for larger volumes of data being sent.
1910 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdySettingsCwnd",
1913 if (bytes_received_
> 10 * 1024) {
1914 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdySettingsCwnd10K",
1917 if (bytes_received_
> 25 * 1024) {
1918 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdySettingsCwnd25K",
1921 if (bytes_received_
> 50 * 1024) {
1922 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdySettingsCwnd50K",
1925 if (bytes_received_
> 100 * 1024) {
1926 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdySettingsCwnd100K",
1934 case SETTINGS_ROUND_TRIP_TIME
:
1935 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdySettingsRTT",
1939 case SETTINGS_DOWNLOAD_RETRANS_RATE
:
1940 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdySettingsRetransRate",
1949 void SpdySession::InvokeUserStreamCreationCallback(
1950 scoped_refptr
<SpdyStream
>* stream
) {
1951 PendingCallbackMap::iterator it
= pending_callback_map_
.find(stream
);
1953 // Exit if the request has already been cancelled.
1954 if (it
== pending_callback_map_
.end())
1957 CompletionCallback callback
= it
->second
.callback
;
1958 int result
= it
->second
.result
;
1959 pending_callback_map_
.erase(it
);
1960 callback
.Run(result
);
1963 SSLClientSocket
* SpdySession::GetSSLClientSocket() const {
1966 SSLClientSocket
* ssl_socket
=
1967 reinterpret_cast<SSLClientSocket
*>(connection_
->socket());