Rename origin_bound_cert files to server_bound_cert.
[chromium-blink-merge.git] / net / spdy / spdy_session.cc
blob7767e4fec8211163cc5081e2a89f960630a33b18
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"
7 #include <map>
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"
39 namespace net {
41 NetLogSpdySynParameter::NetLogSpdySynParameter(
42 const linked_ptr<SpdyHeaderBlock>& headers,
43 SpdyControlFlags flags,
44 SpdyStreamId id,
45 SpdyStreamId associated_stream)
46 : headers_(headers),
47 flags_(flags),
48 id_(id),
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_);
68 return dict;
71 NetLogSpdyCredentialParameter::NetLogSpdyCredentialParameter(
72 size_t slot,
73 const std::string& origin)
74 : slot_(slot),
75 origin_(origin) {
78 NetLogSpdyCredentialParameter::~NetLogSpdyCredentialParameter() {
81 Value* NetLogSpdyCredentialParameter::ToValue() const {
82 DictionaryValue* dict = new DictionaryValue();
83 dict->SetInteger("slot", slot_);
84 dict->SetString("origin", origin_);
85 return dict;
88 NetLogSpdySessionCloseParameter::NetLogSpdySessionCloseParameter(
89 int status,
90 const std::string& description)
91 : status_(status),
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_);
101 return dict;
104 namespace {
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 {
112 public:
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()));
119 return dict;
121 private:
122 const HostPortProxyPair host_pair_;
123 DISALLOW_COPY_AND_ASSIGN(NetLogSpdySessionParameter);
126 class NetLogSpdySettingsParameter : public NetLog::EventParameters {
127 public:
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);
140 return dict;
143 private:
144 ~NetLogSpdySettingsParameter() {}
145 const SpdySettings settings_;
147 DISALLOW_COPY_AND_ASSIGN(NetLogSpdySettingsParameter);
150 class NetLogSpdyWindowUpdateParameter : public NetLog::EventParameters {
151 public:
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_);
159 return dict;
162 private:
163 ~NetLogSpdyWindowUpdateParameter() {}
164 const SpdyStreamId stream_id_;
165 const int32 delta_;
167 DISALLOW_COPY_AND_ASSIGN(NetLogSpdyWindowUpdateParameter);
170 class NetLogSpdyDataParameter : public NetLog::EventParameters {
171 public:
172 NetLogSpdyDataParameter(SpdyStreamId stream_id,
173 int size,
174 SpdyDataFlags flags)
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_));
182 return dict;
185 private:
186 ~NetLogSpdyDataParameter() {}
187 const SpdyStreamId stream_id_;
188 const int size_;
189 const SpdyDataFlags flags_;
191 DISALLOW_COPY_AND_ASSIGN(NetLogSpdyDataParameter);
194 class NetLogSpdyRstParameter : public NetLog::EventParameters {
195 public:
196 NetLogSpdyRstParameter(SpdyStreamId stream_id,
197 int status,
198 const std::string& description)
199 : stream_id_(stream_id),
200 status_(status),
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_);
208 return dict;
211 private:
212 ~NetLogSpdyRstParameter() {}
213 const SpdyStreamId stream_id_;
214 const int status_;
215 const std::string description_;
217 DISALLOW_COPY_AND_ASSIGN(NetLogSpdyRstParameter);
220 class NetLogSpdyPingParameter : public NetLog::EventParameters {
221 public:
222 explicit NetLogSpdyPingParameter(uint32 unique_id, const std::string& type)
223 : unique_id_(unique_id),
224 type_(type) {}
226 virtual Value* ToValue() const {
227 DictionaryValue* dict = new DictionaryValue();
228 dict->SetInteger("unique_id", unique_id_);
229 dict->SetString("type", type_);
230 return dict;
233 private:
234 ~NetLogSpdyPingParameter() {}
235 const uint32 unique_id_;
236 const std::string type_;
238 DISALLOW_COPY_AND_ASSIGN(NetLogSpdyPingParameter);
241 class NetLogSpdyGoAwayParameter : public NetLog::EventParameters {
242 public:
243 NetLogSpdyGoAwayParameter(SpdyStreamId last_stream_id,
244 int active_streams,
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_);
256 return dict;
259 private:
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;
273 } // namespace
275 // static
276 void SpdySession::set_default_protocol(
277 SSLClientSocket::NextProto default_protocol) {
278 g_default_protocol = default_protocol;
281 // static
282 void SpdySession::set_max_concurrent_streams(size_t value) {
283 g_max_concurrent_stream_limit = value;
286 // static
287 void SpdySession::set_enable_ping_based_connection_checking(bool enable) {
288 g_enable_ping_based_connection_checking = enable;
291 // static
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);
297 // static
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,
310 NetLog* net_log)
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),
321 is_secure_(false),
322 certificate_error_code_(OK),
323 error_(OK),
324 state_(IDLE),
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),
330 bytes_received_(0),
331 sent_settings_(false),
332 received_settings_(false),
333 stalled_streams_(0),
334 pings_in_flight_(0),
335 next_ping_id_(1),
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)),
350 hung_interval_(
351 base::TimeDelta::FromSeconds(kHungIntervalSeconds)) {
352 DCHECK(HttpStreamFactory::spdy_enabled());
353 net_log_.BeginEvent(
354 NetLog::TYPE_SPDY_SESSION,
355 make_scoped_refptr(
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) {
366 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());
383 RecordHistograms();
385 net_log_.EndEvent(NetLog::TYPE_SPDY_SESSION, NULL);
388 net::Error SpdySession::InitializeWithSocket(
389 ClientSocketHandle* connection,
390 bool is_secure,
391 int certificate_error_code) {
392 base::StatsCounter spdy_sessions("spdy.sessions");
393 spdy_sessions.Increment();
395 state_ = CONNECTED;
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;
402 if (is_secure_) {
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);
425 SendSettings();
427 // Write out any data that we might have to send, such as the settings frame.
428 WriteSocketLater();
429 net::Error error = ReadSocket();
430 if (error == ERR_IO_PENDING)
431 return OK;
432 return error;
435 bool SpdySession::VerifyDomainAuthentication(const std::string& domain) {
436 if (!verify_domain_authentication_)
437 return true;
439 if (state_ != CONNECTED)
440 return false;
442 SSLInfo ssl_info;
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(
453 const GURL& url,
454 scoped_refptr<SpdyStream>* stream,
455 const BoundNetLog& stream_net_log) {
456 CHECK_NE(state_, CLOSED);
458 *stream = NULL;
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"))) {
464 CloseSessionOnError(
465 static_cast<net::Error>(certificate_error_code_),
466 true,
467 "Tried to get SPDY stream for secure content over an unauthenticated "
468 "session.");
469 return ERR_SPDY_PROTOCOL_ERROR;
472 *stream = GetActivePushStream(url.spec());
473 if (stream->get()) {
474 DCHECK(streams_pushed_and_claimed_count_ < streams_pushed_count_);
475 streams_pushed_and_claimed_count_++;
476 return OK;
478 return 0;
481 int SpdySession::CreateStream(
482 const GURL& url,
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);
492 stalled_streams_++;
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(
518 FROM_HERE,
519 base::Bind(&SpdySession::InvokeUserStreamCreationCallback,
520 weak_factory_.GetWeakPtr(), stream));
521 break;
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);
534 return;
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);
546 // Now copy it back
547 while (!tmp.empty()) {
548 create_stream_queues_[i].push(tmp.front());
549 tmp.pop();
554 int SpdySession::CreateStreamImpl(
555 const GURL& url,
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"))) {
563 CloseSessionOnError(
564 static_cast<net::Error>(certificate_error_code_),
565 true,
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,
576 stream_id,
577 false,
578 stream_net_log);
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());
594 return OK;
597 bool SpdySession::NeedsCredentials(const HostPortPair& origin) const {
598 if (!is_secure_)
599 return false;
600 SSLClientSocket* ssl_socket = GetSSLClientSocket();
601 if (ssl_socket->protocol_negotiated() < SSLClientSocket::kProtoSPDY3)
602 return false;
603 if (!ssl_socket->WasDomainBoundCertSent())
604 return false;
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) {
622 // Find our stream
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(
633 stream_id, 0,
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()) {
643 net_log().AddEvent(
644 NetLog::TYPE_SPDY_SESSION_SYN_STREAM,
645 make_scoped_refptr(
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) {
664 DCHECK(is_secure_);
665 unsigned char secret[32]; // 32 bytes from the spec
666 GetSSLClientSocket()->ExportKeyingMaterial("SPDY certificate proof",
667 true, origin,
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;
677 switch (type) {
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);
689 break;
691 default:
692 NOTREACHED();
695 SpdyCredential credential;
696 GURL origin_url(origin);
697 credential.slot =
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()) {
708 net_log().AddEvent(
709 NetLog::TYPE_SPDY_SESSION_SEND_CREDENTIAL,
710 make_scoped_refptr(
711 new NetLogSpdyCredentialParameter(credential.slot,
712 origin)));
714 return ERR_IO_PENDING;
717 int SpdySession::WriteStreamData(SpdyStreamId stream_id,
718 net::IOBuffer* data, int len,
719 SpdyDataFlags flags) {
720 // Find our stream
721 DCHECK(IsStreamActive(stream_id));
722 scoped_refptr<SpdyStream> stream = active_streams_[stream_id];
723 CHECK_EQ(stream->stream_id(), stream_id);
724 if (!stream)
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.
733 if (flow_control_) {
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
740 // stall occurs.
741 stream->set_stalled_by_flow_control(true);
742 net_log().AddEvent(
743 NetLog::TYPE_SPDY_SESSION_STALLED_ON_SEND_WINDOW,
744 make_scoped_refptr(
745 new NetLogIntegerParameter("stream_id", stream_id)));
746 return ERR_IO_PENDING;
748 int new_len = std::min(len, stream->send_window_size());
749 if (new_len < len) {
750 len = new_len;
751 flags = static_cast<SpdyDataFlags>(flags & ~DATA_FLAG_FIN);
753 stream->DecreaseSendWindowSize(len);
756 if (net_log().IsLoggingAllEvents()) {
757 net_log().AddEvent(
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.
763 if (len > 0)
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.
778 if (len > 0)
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) {
795 net_log().AddEvent(
796 NetLog::TYPE_SPDY_SESSION_SEND_RST_STREAM,
797 make_scoped_refptr(new NetLogSpdyRstParameter(stream_id, status,
798 description)));
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.
805 int priority = 3;
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
824 // LoadState.
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
835 // buffer (32KB).
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);
843 if (bytes_read == 0)
844 error = ERR_CONNECTION_CLOSED;
845 CloseSessionOnError(error, true, "bytes_read is <= 0.");
846 return;
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
856 // cleanup.
857 scoped_refptr<SpdySession> self(this);
859 DCHECK(buffered_spdy_framer_.get());
860 char *data = read_buffer_->data();
861 while (bytes_read &&
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)
873 ReadSocket();
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();
884 if (result >= 0) {
885 // It should not be possible to have written more bytes than our
886 // in_flight_write_.
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()) {
893 if (stream) {
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
897 // size.
898 if (result > 0) {
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
905 // to the socket.
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
916 // message loop).
917 WriteSocketLater();
918 } else {
919 in_flight_write_.release();
921 // The stream is now errored. Close it down.
922 CloseSessionOnError(
923 static_cast<net::Error>(result), true, "The stream has errored.");
927 net::Error SpdySession::ReadSocket() {
928 if (read_pending_)
929 return OK;
931 if (state_ == CLOSED) {
932 NOTREACHED();
933 return ERR_UNEXPECTED;
936 CHECK(connection_.get());
937 CHECK(connection_->socket());
938 int bytes_read = connection_->socket()->Read(
939 read_buffer_.get(),
940 kReadBufferSize,
941 base::Bind(&SpdySession::OnReadComplete, base::Unretained(this)));
942 switch (bytes_read) {
943 case 0:
944 // Socket is closed!
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;
951 default:
952 // Data was read, process it.
953 // Schedule the work through the message loop to avoid recursive
954 // callbacks.
955 read_pending_ = true;
956 MessageLoop::current()->PostTask(
957 FROM_HERE,
958 base::Bind(&SpdySession::OnReadComplete,
959 weak_factory_.GetWeakPtr(), bytes_read));
960 break;
962 return OK;
965 void SpdySession::WriteSocketLater() {
966 if (delayed_write_pending_)
967 return;
969 if (state_ < CONNECTED)
970 return;
972 delayed_write_pending_ = true;
973 MessageLoop::current()->PostTask(
974 FROM_HERE,
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)
987 return;
989 if (write_pending_) // Another write is in progress still.
990 return;
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();
999 queue_.pop();
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);
1004 size_t size;
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.");
1011 return;
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());
1024 } else {
1025 size = uncompressed_frame.length() + SpdyFrame::kHeaderSize;
1026 in_flight_write_ = next_buffer;
1028 } else {
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)
1038 break;
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.
1045 if (rv < 0)
1046 break;
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;
1074 DCHECK(stream);
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())
1083 queue_.pop();
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;
1091 return id;
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));
1102 WriteSocketLater();
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);
1112 DCHECK_LT(err, OK);
1113 net_log_.AddEvent(
1114 NetLog::TYPE_SPDY_SESSION_CLOSE,
1115 make_scoped_refptr(
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
1120 // an error.
1121 if (state_ != CLOSED) {
1122 state_ = CLOSED;
1123 error_ = err;
1124 if (remove_from_pool)
1125 RemoveFromPool();
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;
1155 if (is_secure_) {
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_);
1174 return dict;
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.
1199 RemoveFromPool();
1200 // Since the underlying socket is only returned when |this| is destroyed
1201 // we should only return true if RemoveFromPool() removed the last ref.
1202 return ret;
1204 return false;
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.
1218 if (status != OK) {
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);
1225 break;
1230 // The stream might have been deleted.
1231 ActiveStreamMap::iterator it2 = active_streams_.find(id);
1232 if (it2 == active_streams_.end())
1233 return;
1235 // If this is an active stream, call the callback.
1236 const scoped_refptr<SpdyStream> stream(it2->second);
1237 active_streams_.erase(it2);
1238 if (stream)
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();
1261 return stream;
1263 return NULL;
1266 bool SpdySession::GetSSLInfo(SSLInfo* ssl_info,
1267 bool* was_npn_negotiated,
1268 SSLClientSocket::NextProto* protocol_negotiated) {
1269 if (!is_secure_) {
1270 *protocol_negotiated = SSLClientSocket::kProtoUnknown;
1271 return false;
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();
1277 return true;
1280 bool SpdySession::GetSSLCertRequestInfo(
1281 SSLCertRequestInfo* cert_request_info) {
1282 if (!is_secure_)
1283 return false;
1284 GetSSLClientSocket()->GetSSLCertRequestInfo(cert_request_info);
1285 return true;
1288 ServerBoundCertService* SpdySession::GetServerBoundCertService() const {
1289 if (!is_secure_)
1290 return NULL;
1291 return GetSSLClientSocket()->GetServerBoundCertService();
1294 SSLClientCertType SpdySession::GetDomainBoundCertType() const {
1295 if (!is_secure_)
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,
1313 const char* data,
1314 size_t len) {
1315 if (net_log().IsLoggingAllEvents()) {
1316 net_log().AddEvent(
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.
1324 return;
1327 scoped_refptr<SpdyStream> stream = active_streams_[stream_id];
1328 stream->OnDataReceived(data, len);
1331 void SpdySession::OnSetting(SpdySettingsIds id,
1332 uint8 flags,
1333 uint32 value) {
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));
1345 net_log_.AddEvent(
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) {
1352 int rv = OK;
1353 rv = stream->OnResponseReceived(headers);
1354 if (rv < 0) {
1355 DCHECK_NE(rv, ERR_IO_PENDING);
1356 const SpdyStreamId stream_id = stream->stream_id();
1357 DeleteStream(stream_id, rv);
1358 return false;
1360 return true;
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()) {
1370 net_log_.AddEvent(
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;
1380 return;
1383 if (IsStreamActive(stream_id)) {
1384 LOG(WARNING) << "Received OnSyn for active stream " << stream_id;
1385 return;
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);
1393 return;
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 : "";
1403 if (url.empty()) {
1404 ResetStream(stream_id, PROTOCOL_ERROR,
1405 "Pushed stream did not contain a url.");
1406 return;
1409 GURL gurl(url);
1410 if (!gurl.is_valid()) {
1411 ResetStream(stream_id, PROTOCOL_ERROR,
1412 "Pushed stream url was invalid: " + url);
1413 return;
1416 // Verify we have a valid stream association.
1417 if (!IsStreamActive(associated_stream_id)) {
1418 ResetStream(stream_id, INVALID_ASSOCIATED_STREAM,
1419 base::StringPrintf(
1420 "Received OnSyn with inactive associated stream %d",
1421 associated_stream_id));
1422 return;
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,
1430 base::StringPrintf(
1431 "Rejected Cross Origin Push Stream %d",
1432 associated_stream_id));
1433 return;
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);
1441 return;
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))
1458 return;
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()) {
1469 net_log().AddEvent(
1470 NetLog::TYPE_SPDY_SESSION_SYN_REPLY,
1471 make_scoped_refptr(new NetLogSpdySynParameter(
1472 headers, static_cast<SpdyControlFlags>(frame.flags()),
1473 stream_id, 0)));
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;
1480 return;
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);
1491 return;
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()) {
1503 net_log().AddEvent(
1504 NetLog::TYPE_SPDY_SESSION_HEADERS,
1505 make_scoped_refptr(new NetLogSpdySynParameter(
1506 headers, static_cast<SpdyControlFlags>(frame.flags()),
1507 stream_id, 0)));
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;
1514 return;
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);
1522 if (rv < 0) {
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();
1532 net_log().AddEvent(
1533 NetLog::TYPE_SPDY_SESSION_RST_STREAM,
1534 make_scoped_refptr(
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;
1541 return;
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);
1551 } else {
1552 stream->LogStreamError(ERR_SPDY_PROTOCOL_ERROR,
1553 base::StringPrintf("SPDY stream closed: %d",
1554 frame.status()));
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) {
1562 net_log_.AddEvent(
1563 NetLog::TYPE_SPDY_SESSION_GOAWAY,
1564 make_scoped_refptr(
1565 new NetLogSpdyGoAwayParameter(frame.last_accepted_stream_id(),
1566 active_streams_.size(),
1567 unclaimed_pushed_streams_.size())));
1568 RemoveFromPool();
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
1576 // closed.
1579 void SpdySession::OnPing(const SpdyPingControlFrame& frame) {
1580 net_log_.AddEvent(
1581 NetLog::TYPE_SPDY_SESSION_PING,
1582 make_scoped_refptr(
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());
1588 return;
1591 --pings_in_flight_;
1592 if (pings_in_flight_ < 0) {
1593 CloseSessionOnError(
1594 net::ERR_SPDY_PROTOCOL_ERROR, true, "pings_in_flight_ is < 0.");
1595 return;
1598 if (pings_in_flight_ > 0)
1599 return;
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_)
1606 return;
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());
1615 net_log_.AddEvent(
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;
1622 return;
1625 if (delta_window_size < 1) {
1626 ResetStream(stream_id, FLOW_CONTROL_ERROR,
1627 base::StringPrintf(
1628 "Received WINDOW_UPDATE with an invalid "
1629 "delta_window_size %d", delta_window_size));
1630 return;
1633 scoped_refptr<SpdyStream> stream = active_streams_[stream_id];
1634 CHECK_EQ(stream->stream_id(), stream_id);
1635 CHECK(!stream->cancelled());
1637 if (flow_control_)
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);
1647 net_log_.AddEvent(
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");
1662 if (!trial) {
1663 LOG(WARNING) << "Could not find \"SpdyCwnd\" in FieldTrialList";
1664 return cwnd;
1666 if (trial->group_name() == "cwnd10")
1667 return 10;
1668 else if (trial->group_name() == "cwnd16")
1669 return 16;
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")
1675 return cwnd;
1676 NOTREACHED();
1677 return cwnd;
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())
1687 return;
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;
1697 switch (id) {
1698 case SETTINGS_CURRENT_CWND:
1699 uint32 cwnd = 0;
1700 cwnd = ApplyCwndFieldTrialPolicy(val);
1701 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdySettingsCwndSent",
1702 cwnd,
1703 1, 200, 100);
1704 if (cwnd != val) {
1705 SettingsFlagsAndId new_id(SETTINGS_FLAG_PLEASE_PERSIST,
1706 id);
1707 i->second = cwnd;
1708 i->first = new_id;
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;
1713 continue;
1716 unique_settings[id] = *i;
1719 HandleSettings(settings);
1721 net_log_.AddEvent(
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;
1728 ++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) {
1748 switch (id) {
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();
1753 break;
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);
1763 break;
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;
1771 DCHECK(stream);
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)
1779 return;
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_)
1784 SendPrefacePing();
1786 PlanToSendTrailingPing();
1789 void SpdySession::SendPrefacePing() {
1790 WritePingFrame(next_ping_id_);
1793 void SpdySession::PlanToSendTrailingPing() {
1794 if (trailing_ping_pending_)
1795 return;
1797 trailing_ping_pending_ = true;
1798 MessageLoop::current()->PostDelayedTask(
1799 FROM_HERE,
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_));
1814 QueueFrame(
1815 ping_frame.get(), buffered_spdy_framer_->GetHighestPriority(), NULL);
1817 if (net_log().IsLoggingAllEvents()) {
1818 net_log().AddEvent(
1819 NetLog::TYPE_SPDY_SESSION_PING,
1820 make_scoped_refptr(new NetLogSpdyPingParameter(next_ping_id_, "sent")));
1822 if (unique_id % 2 != 0) {
1823 next_ping_id_ += 2;
1824 ++pings_in_flight_;
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_)
1833 return;
1835 check_ping_status_pending_ = true;
1836 MessageLoop::current()->PostDelayedTask(
1837 FROM_HERE,
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;
1846 return;
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);
1860 return;
1863 // Check the status of connection after a delay.
1864 MessageLoop::current()->PostDelayedTask(
1865 FROM_HERE,
1866 base::Bind(&SpdySession::CheckPingStatus, weak_factory_.GetWeakPtr(),
1867 now),
1868 delay);
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_,
1878 0, 300, 50);
1879 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsPushedPerSession",
1880 streams_pushed_count_,
1881 0, 300, 50);
1882 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsPushedAndClaimedPerSession",
1883 streams_pushed_and_claimed_count_,
1884 0, 300, 50);
1885 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsAbandonedPerSession",
1886 streams_abandoned_count_,
1887 0, 300, 50);
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",
1893 stalled_streams_,
1894 0, 300, 50);
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",
1911 setting.second,
1912 1, 200, 100);
1913 if (bytes_received_ > 10 * 1024) {
1914 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdySettingsCwnd10K",
1915 setting.second,
1916 1, 200, 100);
1917 if (bytes_received_ > 25 * 1024) {
1918 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdySettingsCwnd25K",
1919 setting.second,
1920 1, 200, 100);
1921 if (bytes_received_ > 50 * 1024) {
1922 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdySettingsCwnd50K",
1923 setting.second,
1924 1, 200, 100);
1925 if (bytes_received_ > 100 * 1024) {
1926 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdySettingsCwnd100K",
1927 setting.second,
1928 1, 200, 100);
1933 break;
1934 case SETTINGS_ROUND_TRIP_TIME:
1935 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdySettingsRTT",
1936 setting.second,
1937 1, 1200, 100);
1938 break;
1939 case SETTINGS_DOWNLOAD_RETRANS_RATE:
1940 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdySettingsRetransRate",
1941 setting.second,
1942 1, 100, 50);
1943 break;
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())
1955 return;
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 {
1964 if (!is_secure_)
1965 return NULL;
1966 SSLClientSocket* ssl_socket =
1967 reinterpret_cast<SSLClientSocket*>(connection_->socket());
1968 DCHECK(ssl_socket);
1969 return ssl_socket;
1972 } // namespace net