summaryrefslogtreecommitdiffstats
path: root/chromium/net/spdy/spdy_session.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/spdy/spdy_session.cc')
-rw-r--r--chromium/net/spdy/spdy_session.cc1134
1 files changed, 655 insertions, 479 deletions
diff --git a/chromium/net/spdy/spdy_session.cc b/chromium/net/spdy/spdy_session.cc
index 9508f0e6af4..0310564a7e9 100644
--- a/chromium/net/spdy/spdy_session.cc
+++ b/chromium/net/spdy/spdy_session.cc
@@ -29,8 +29,10 @@
#include "net/base/net_log.h"
#include "net/base/net_util.h"
#include "net/cert/asn1_util.h"
+#include "net/http/http_log_util.h"
#include "net/http/http_network_session.h"
#include "net/http/http_server_properties.h"
+#include "net/http/http_util.h"
#include "net/spdy/spdy_buffer_producer.h"
#include "net/spdy/spdy_frame_builder.h"
#include "net/spdy/spdy_http_utils.h"
@@ -38,6 +40,8 @@
#include "net/spdy/spdy_session_pool.h"
#include "net/spdy/spdy_stream.h"
#include "net/ssl/server_bound_cert_service.h"
+#include "net/ssl/ssl_cipher_suite_names.h"
+#include "net/ssl/ssl_connection_status_flags.h"
namespace net {
@@ -47,20 +51,18 @@ const int kReadBufferSize = 8 * 1024;
const int kDefaultConnectionAtRiskOfLossSeconds = 10;
const int kHungIntervalSeconds = 10;
-// Always start at 1 for the first stream id.
-const SpdyStreamId kFirstStreamId = 1;
-
// Minimum seconds that unclaimed pushed streams will be kept in memory.
const int kMinPushedStreamLifetimeSeconds = 300;
scoped_ptr<base::ListValue> SpdyHeaderBlockToListValue(
- const SpdyHeaderBlock& headers) {
+ const SpdyHeaderBlock& headers,
+ net::NetLog::LogLevel log_level) {
scoped_ptr<base::ListValue> headers_list(new base::ListValue());
for (SpdyHeaderBlock::const_iterator it = headers.begin();
it != headers.end(); ++it) {
headers_list->AppendString(
it->first + ": " +
- (ShouldShowHttpHeaderValue(it->first) ? it->second : "[elided]"));
+ ElideHeaderValueForNetLog(log_level, it->first, it->second));
}
return headers_list.Pass();
}
@@ -70,9 +72,10 @@ base::Value* NetLogSpdySynStreamSentCallback(const SpdyHeaderBlock* headers,
bool unidirectional,
SpdyPriority spdy_priority,
SpdyStreamId stream_id,
- NetLog::LogLevel /* log_level */) {
+ NetLog::LogLevel log_level) {
base::DictionaryValue* dict = new base::DictionaryValue();
- dict->Set("headers", SpdyHeaderBlockToListValue(*headers).release());
+ dict->Set("headers",
+ SpdyHeaderBlockToListValue(*headers, log_level).release());
dict->SetBoolean("fin", fin);
dict->SetBoolean("unidirectional", unidirectional);
dict->SetInteger("spdy_priority", static_cast<int>(spdy_priority));
@@ -87,9 +90,10 @@ base::Value* NetLogSpdySynStreamReceivedCallback(
SpdyPriority spdy_priority,
SpdyStreamId stream_id,
SpdyStreamId associated_stream,
- NetLog::LogLevel /* log_level */) {
+ NetLog::LogLevel log_level) {
base::DictionaryValue* dict = new base::DictionaryValue();
- dict->Set("headers", SpdyHeaderBlockToListValue(*headers).release());
+ dict->Set("headers",
+ SpdyHeaderBlockToListValue(*headers, log_level).release());
dict->SetBoolean("fin", fin);
dict->SetBoolean("unidirectional", unidirectional);
dict->SetInteger("spdy_priority", static_cast<int>(spdy_priority));
@@ -102,9 +106,10 @@ base::Value* NetLogSpdySynReplyOrHeadersReceivedCallback(
const SpdyHeaderBlock* headers,
bool fin,
SpdyStreamId stream_id,
- NetLog::LogLevel /* log_level */) {
+ NetLog::LogLevel log_level) {
base::DictionaryValue* dict = new base::DictionaryValue();
- dict->Set("headers", SpdyHeaderBlockToListValue(*headers).release());
+ dict->Set("headers",
+ SpdyHeaderBlockToListValue(*headers, log_level).release());
dict->SetBoolean("fin", fin);
dict->SetInteger("stream_id", stream_id);
return dict;
@@ -205,12 +210,14 @@ base::Value* NetLogSpdyRstCallback(SpdyStreamId stream_id,
return dict;
}
-base::Value* NetLogSpdyPingCallback(uint32 unique_id,
+base::Value* NetLogSpdyPingCallback(SpdyPingId unique_id,
+ bool is_ack,
const char* type,
NetLog::LogLevel /* log_level */) {
base::DictionaryValue* dict = new base::DictionaryValue();
dict->SetInteger("unique_id", unique_id);
dict->SetString("type", type);
+ dict->SetBoolean("is_ack", is_ack);
return dict;
}
@@ -228,6 +235,19 @@ base::Value* NetLogSpdyGoAwayCallback(SpdyStreamId last_stream_id,
return dict;
}
+base::Value* NetLogSpdyPushPromiseReceivedCallback(
+ const SpdyHeaderBlock* headers,
+ SpdyStreamId stream_id,
+ SpdyStreamId promised_stream_id,
+ NetLog::LogLevel log_level) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->Set("headers",
+ SpdyHeaderBlockToListValue(*headers, log_level).release());
+ dict->SetInteger("id", stream_id);
+ dict->SetInteger("promised_stream_id", promised_stream_id);
+ return dict;
+}
+
// Helper function to return the total size of an array of objects
// with .size() member functions.
template <typename T, size_t N> size_t GetTotalSize(const T (&arr)[N]) {
@@ -259,6 +279,151 @@ const size_t kMaxConcurrentStreamLimit = 256;
} // namespace
+SpdyProtocolErrorDetails MapFramerErrorToProtocolError(
+ SpdyFramer::SpdyError err) {
+ switch(err) {
+ case SpdyFramer::SPDY_NO_ERROR:
+ return SPDY_ERROR_NO_ERROR;
+ case SpdyFramer::SPDY_INVALID_CONTROL_FRAME:
+ return SPDY_ERROR_INVALID_CONTROL_FRAME;
+ case SpdyFramer::SPDY_CONTROL_PAYLOAD_TOO_LARGE:
+ return SPDY_ERROR_CONTROL_PAYLOAD_TOO_LARGE;
+ case SpdyFramer::SPDY_ZLIB_INIT_FAILURE:
+ return SPDY_ERROR_ZLIB_INIT_FAILURE;
+ case SpdyFramer::SPDY_UNSUPPORTED_VERSION:
+ return SPDY_ERROR_UNSUPPORTED_VERSION;
+ case SpdyFramer::SPDY_DECOMPRESS_FAILURE:
+ return SPDY_ERROR_DECOMPRESS_FAILURE;
+ case SpdyFramer::SPDY_COMPRESS_FAILURE:
+ return SPDY_ERROR_COMPRESS_FAILURE;
+ case SpdyFramer::SPDY_GOAWAY_FRAME_CORRUPT:
+ return SPDY_ERROR_GOAWAY_FRAME_CORRUPT;
+ case SpdyFramer::SPDY_RST_STREAM_FRAME_CORRUPT:
+ return SPDY_ERROR_RST_STREAM_FRAME_CORRUPT;
+ case SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS:
+ return SPDY_ERROR_INVALID_DATA_FRAME_FLAGS;
+ case SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS:
+ return SPDY_ERROR_INVALID_CONTROL_FRAME_FLAGS;
+ case SpdyFramer::SPDY_UNEXPECTED_FRAME:
+ return SPDY_ERROR_UNEXPECTED_FRAME;
+ default:
+ NOTREACHED();
+ return static_cast<SpdyProtocolErrorDetails>(-1);
+ }
+}
+
+Error MapFramerErrorToNetError(SpdyFramer::SpdyError err) {
+ switch (err) {
+ case SpdyFramer::SPDY_NO_ERROR:
+ return OK;
+ case SpdyFramer::SPDY_INVALID_CONTROL_FRAME:
+ return ERR_SPDY_PROTOCOL_ERROR;
+ case SpdyFramer::SPDY_CONTROL_PAYLOAD_TOO_LARGE:
+ return ERR_SPDY_FRAME_SIZE_ERROR;
+ case SpdyFramer::SPDY_ZLIB_INIT_FAILURE:
+ return ERR_SPDY_COMPRESSION_ERROR;
+ case SpdyFramer::SPDY_UNSUPPORTED_VERSION:
+ return ERR_SPDY_PROTOCOL_ERROR;
+ case SpdyFramer::SPDY_DECOMPRESS_FAILURE:
+ return ERR_SPDY_COMPRESSION_ERROR;
+ case SpdyFramer::SPDY_COMPRESS_FAILURE:
+ return ERR_SPDY_COMPRESSION_ERROR;
+ case SpdyFramer::SPDY_GOAWAY_FRAME_CORRUPT:
+ return ERR_SPDY_PROTOCOL_ERROR;
+ case SpdyFramer::SPDY_RST_STREAM_FRAME_CORRUPT:
+ return ERR_SPDY_PROTOCOL_ERROR;
+ case SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS:
+ return ERR_SPDY_PROTOCOL_ERROR;
+ case SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS:
+ return ERR_SPDY_PROTOCOL_ERROR;
+ case SpdyFramer::SPDY_UNEXPECTED_FRAME:
+ return ERR_SPDY_PROTOCOL_ERROR;
+ default:
+ NOTREACHED();
+ return ERR_SPDY_PROTOCOL_ERROR;
+ }
+}
+
+SpdyProtocolErrorDetails MapRstStreamStatusToProtocolError(
+ SpdyRstStreamStatus status) {
+ switch(status) {
+ case RST_STREAM_PROTOCOL_ERROR:
+ return STATUS_CODE_PROTOCOL_ERROR;
+ case RST_STREAM_INVALID_STREAM:
+ return STATUS_CODE_INVALID_STREAM;
+ case RST_STREAM_REFUSED_STREAM:
+ return STATUS_CODE_REFUSED_STREAM;
+ case RST_STREAM_UNSUPPORTED_VERSION:
+ return STATUS_CODE_UNSUPPORTED_VERSION;
+ case RST_STREAM_CANCEL:
+ return STATUS_CODE_CANCEL;
+ case RST_STREAM_INTERNAL_ERROR:
+ return STATUS_CODE_INTERNAL_ERROR;
+ case RST_STREAM_FLOW_CONTROL_ERROR:
+ return STATUS_CODE_FLOW_CONTROL_ERROR;
+ case RST_STREAM_STREAM_IN_USE:
+ return STATUS_CODE_STREAM_IN_USE;
+ case RST_STREAM_STREAM_ALREADY_CLOSED:
+ return STATUS_CODE_STREAM_ALREADY_CLOSED;
+ case RST_STREAM_INVALID_CREDENTIALS:
+ return STATUS_CODE_INVALID_CREDENTIALS;
+ case RST_STREAM_FRAME_SIZE_ERROR:
+ return STATUS_CODE_FRAME_SIZE_ERROR;
+ case RST_STREAM_SETTINGS_TIMEOUT:
+ return STATUS_CODE_SETTINGS_TIMEOUT;
+ case RST_STREAM_CONNECT_ERROR:
+ return STATUS_CODE_CONNECT_ERROR;
+ case RST_STREAM_ENHANCE_YOUR_CALM:
+ return STATUS_CODE_ENHANCE_YOUR_CALM;
+ default:
+ NOTREACHED();
+ return static_cast<SpdyProtocolErrorDetails>(-1);
+ }
+}
+
+SpdyGoAwayStatus MapNetErrorToGoAwayStatus(Error err) {
+ switch (err) {
+ case OK:
+ return GOAWAY_NO_ERROR;
+ case ERR_SPDY_PROTOCOL_ERROR:
+ return GOAWAY_PROTOCOL_ERROR;
+ case ERR_SPDY_FLOW_CONTROL_ERROR:
+ return GOAWAY_FLOW_CONTROL_ERROR;
+ case ERR_SPDY_FRAME_SIZE_ERROR:
+ return GOAWAY_FRAME_SIZE_ERROR;
+ case ERR_SPDY_COMPRESSION_ERROR:
+ return GOAWAY_COMPRESSION_ERROR;
+ case ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY:
+ return GOAWAY_INADEQUATE_SECURITY;
+ default:
+ return GOAWAY_PROTOCOL_ERROR;
+ }
+}
+
+void SplitPushedHeadersToRequestAndResponse(const SpdyHeaderBlock& headers,
+ SpdyMajorVersion protocol_version,
+ SpdyHeaderBlock* request_headers,
+ SpdyHeaderBlock* response_headers) {
+ DCHECK(response_headers);
+ DCHECK(request_headers);
+ for (SpdyHeaderBlock::const_iterator it = headers.begin();
+ it != headers.end();
+ ++it) {
+ SpdyHeaderBlock* to_insert = response_headers;
+ if (protocol_version == SPDY2) {
+ if (it->first == "url")
+ to_insert = request_headers;
+ } else {
+ const char* host = protocol_version >= SPDY4 ? ":authority" : ":host";
+ static const char* scheme = ":scheme";
+ static const char* path = ":path";
+ if (it->first == host || it->first == scheme || it->first == path)
+ to_insert = request_headers;
+ }
+ to_insert->insert(*it);
+ }
+}
+
SpdyStreamRequest::SpdyStreamRequest() : weak_ptr_factory_(this) {
Reset();
}
@@ -349,7 +514,8 @@ SpdySession::ActiveStreamInfo::ActiveStreamInfo()
SpdySession::ActiveStreamInfo::ActiveStreamInfo(SpdyStream* stream)
: stream(stream),
- waiting_for_syn_reply(stream->type() != SPDY_PUSH_STREAM) {}
+ waiting_for_syn_reply(stream->type() != SPDY_PUSH_STREAM) {
+}
SpdySession::ActiveStreamInfo::~ActiveStreamInfo() {}
@@ -377,8 +543,7 @@ SpdySession::SpdySession(
TimeFunc time_func,
const HostPortPair& trusted_spdy_proxy,
NetLog* net_log)
- : weak_factory_(this),
- in_io_loop_(false),
+ : in_io_loop_(false),
spdy_session_key_(spdy_session_key),
pool_(NULL),
http_server_properties_(http_server_properties),
@@ -392,12 +557,12 @@ SpdySession::SpdySession(
read_state_(READ_STATE_DO_READ),
write_state_(WRITE_STATE_IDLE),
error_on_close_(OK),
- max_concurrent_streams_(initial_max_concurrent_streams == 0 ?
- kInitialMaxConcurrentStreams :
- initial_max_concurrent_streams),
- max_concurrent_streams_limit_(max_concurrent_streams_limit == 0 ?
- kMaxConcurrentStreamLimit :
- max_concurrent_streams_limit),
+ max_concurrent_streams_(initial_max_concurrent_streams == 0
+ ? kInitialMaxConcurrentStreams
+ : initial_max_concurrent_streams),
+ max_concurrent_streams_limit_(max_concurrent_streams_limit == 0
+ ? kMaxConcurrentStreamLimit
+ : max_concurrent_streams_limit),
streams_initiated_count_(0),
streams_pushed_count_(0),
streams_pushed_and_claimed_count_(0),
@@ -414,9 +579,9 @@ SpdySession::SpdySession(
send_connection_header_prefix_(false),
flow_control_state_(FLOW_CONTROL_NONE),
stream_initial_send_window_size_(kSpdyStreamInitialWindowSize),
- stream_initial_recv_window_size_(stream_initial_recv_window_size == 0 ?
- kDefaultInitialRecvWindowSize :
- stream_initial_recv_window_size),
+ stream_initial_recv_window_size_(stream_initial_recv_window_size == 0
+ ? kDefaultInitialRecvWindowSize
+ : stream_initial_recv_window_size),
session_send_window_size_(0),
session_recv_window_size_(0),
session_unacked_recv_window_bytes_(0),
@@ -429,10 +594,10 @@ SpdySession::SpdySession(
protocol_(default_protocol),
connection_at_risk_of_loss_time_(
base::TimeDelta::FromSeconds(kDefaultConnectionAtRiskOfLossSeconds)),
- hung_interval_(
- base::TimeDelta::FromSeconds(kHungIntervalSeconds)),
+ hung_interval_(base::TimeDelta::FromSeconds(kHungIntervalSeconds)),
trusted_spdy_proxy_(trusted_spdy_proxy),
- time_func_(time_func) {
+ time_func_(time_func),
+ weak_factory_(this) {
DCHECK_GE(protocol_, kProtoSPDYMinimumVersion);
DCHECK_LE(protocol_, kProtoSPDYMaximumVersion);
DCHECK(HttpStreamFactory::spdy_enabled());
@@ -446,8 +611,7 @@ SpdySession::SpdySession(
SpdySession::~SpdySession() {
CHECK(!in_io_loop_);
- DCHECK(!pool_);
- DcheckClosed();
+ DcheckDraining();
// TODO(akalin): Check connection->is_initialized() instead. This
// requires re-working CreateFakeSpdySession(), though.
@@ -460,7 +624,7 @@ SpdySession::~SpdySession() {
net_log_.EndEvent(NetLog::TYPE_SPDY_SESSION);
}
-Error SpdySession::InitializeWithSocket(
+void SpdySession::InitializeWithSocket(
scoped_ptr<ClientSocketHandle> connection,
SpdySessionPool* pool,
bool is_secure,
@@ -492,7 +656,7 @@ Error SpdySession::InitializeWithSocket(
DCHECK_GE(protocol_, kProtoSPDYMinimumVersion);
DCHECK_LE(protocol_, kProtoSPDYMaximumVersion);
- if (protocol_ == kProtoHTTP2Draft04)
+ if (protocol_ == kProtoSPDY4)
send_connection_header_prefix_ = true;
if (protocol_ >= kProtoSPDY31) {
@@ -521,26 +685,24 @@ Error SpdySession::InitializeWithSocket(
NetLog::TYPE_SPDY_SESSION_INITIALIZED,
connection_->socket()->NetLog().source().ToEventParametersCallback());
- int error = DoReadLoop(READ_STATE_DO_READ, OK);
- if (error == ERR_IO_PENDING)
- error = OK;
- if (error == OK) {
- DCHECK_NE(availability_state_, STATE_CLOSED);
- connection_->AddHigherLayeredPool(this);
- if (enable_sending_initial_data_)
- SendInitialData();
- pool_ = pool;
- } else {
- DcheckClosed();
- }
- return static_cast<Error>(error);
+ DCHECK_EQ(availability_state_, STATE_AVAILABLE);
+ connection_->AddHigherLayeredPool(this);
+ if (enable_sending_initial_data_)
+ SendInitialData();
+ pool_ = pool;
+
+ // Bootstrap the read loop.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&SpdySession::PumpReadLoop,
+ weak_factory_.GetWeakPtr(), READ_STATE_DO_READ, OK));
}
bool SpdySession::VerifyDomainAuthentication(const std::string& domain) {
if (!verify_domain_authentication_)
return true;
- if (availability_state_ == STATE_CLOSED)
+ if (availability_state_ == STATE_DRAINING)
return false;
SSLInfo ssl_info;
@@ -549,6 +711,10 @@ bool SpdySession::VerifyDomainAuthentication(const std::string& domain) {
if (!GetSSLInfo(&ssl_info, &was_npn_negotiated, &protocol_negotiated))
return true; // This is not a secure session, so all domains are okay.
+ // Disable pooling for secure sessions.
+ // TODO(rch): re-enable this.
+ return false;
+#if 0
bool unused = false;
return
!ssl_info.client_cert_sent &&
@@ -556,6 +722,7 @@ bool SpdySession::VerifyDomainAuthentication(const std::string& domain) {
(ServerBoundCertService::GetDomainForHost(domain) ==
ServerBoundCertService::GetDomainForHost(host_port_pair().host()))) &&
ssl_info.cert->VerifyNameMatch(domain, &unused);
+#endif
}
int SpdySession::GetPushStream(
@@ -566,8 +733,7 @@ int SpdySession::GetPushStream(
stream->reset();
- // TODO(akalin): Add unit test exercising this code path.
- if (availability_state_ == STATE_CLOSED)
+ if (availability_state_ == STATE_DRAINING)
return ERR_CONNECTION_CLOSED;
Error err = TryAccessStream(url);
@@ -587,17 +753,14 @@ int SpdySession::GetPushStream(
// another being closed due to received data.
Error SpdySession::TryAccessStream(const GURL& url) {
- DCHECK_NE(availability_state_, STATE_CLOSED);
-
if (is_secure_ && certificate_error_code_ != OK &&
(url.SchemeIs("https") || url.SchemeIs("wss"))) {
RecordProtocolErrorHistogram(
PROTOCOL_ERROR_REQUEST_FOR_SECURE_CONTENT_OVER_INSECURE_SESSION);
- CloseSessionResult result = DoCloseSession(
+ DoDrainSession(
static_cast<Error>(certificate_error_code_),
"Tried to get SPDY stream for secure content over an unauthenticated "
"session.");
- DCHECK_EQ(result, SESSION_CLOSED_AND_REMOVED);
return ERR_SPDY_PROTOCOL_ERROR;
}
return OK;
@@ -611,8 +774,7 @@ int SpdySession::TryCreateStream(
if (availability_state_ == STATE_GOING_AWAY)
return ERR_FAILED;
- // TODO(akalin): Add unit test exercising this code path.
- if (availability_state_ == STATE_CLOSED)
+ if (availability_state_ == STATE_DRAINING)
return ERR_CONNECTION_CLOSED;
Error err = TryAccessStream(request->url());
@@ -642,8 +804,7 @@ int SpdySession::CreateStream(const SpdyStreamRequest& request,
if (availability_state_ == STATE_GOING_AWAY)
return ERR_FAILED;
- // TODO(akalin): Add unit test exercising this code path.
- if (availability_state_ == STATE_CLOSED)
+ if (availability_state_ == STATE_DRAINING)
return ERR_CONNECTION_CLOSED;
Error err = TryAccessStream(request.url());
@@ -659,10 +820,9 @@ int SpdySession::CreateStream(const SpdyStreamRequest& request,
UMA_HISTOGRAM_BOOLEAN("Net.SpdySession.CreateStreamWithSocketConnected",
connection_->socket()->IsConnected());
if (!connection_->socket()->IsConnected()) {
- CloseSessionResult result = DoCloseSession(
+ DoDrainSession(
ERR_CONNECTION_CLOSED,
"Tried to create SPDY stream for a closed socket connection.");
- DCHECK_EQ(result, SESSION_CLOSED_AND_REMOVED);
return ERR_CONNECTION_CLOSED;
}
}
@@ -690,17 +850,17 @@ void SpdySession::CancelStreamRequest(
CHECK_GE(priority, MINIMUM_PRIORITY);
CHECK_LE(priority, MAXIMUM_PRIORITY);
- if (DCHECK_IS_ON()) {
- // |request| should not be in a queue not matching its priority.
- for (int i = MINIMUM_PRIORITY; i <= MAXIMUM_PRIORITY; ++i) {
- if (priority == i)
- continue;
- PendingStreamRequestQueue* queue = &pending_create_stream_queues_[i];
- DCHECK(std::find_if(queue->begin(),
- queue->end(),
- RequestEquals(request)) == queue->end());
- }
+#if DCHECK_IS_ON
+ // |request| should not be in a queue not matching its priority.
+ for (int i = MINIMUM_PRIORITY; i <= MAXIMUM_PRIORITY; ++i) {
+ if (priority == i)
+ continue;
+ PendingStreamRequestQueue* queue = &pending_create_stream_queues_[i];
+ DCHECK(std::find_if(queue->begin(),
+ queue->end(),
+ RequestEquals(request)) == queue->end());
}
+#endif
PendingStreamRequestQueue* queue =
&pending_create_stream_queues_[priority];
@@ -749,6 +909,9 @@ void SpdySession::ProcessPendingStreamRequests() {
if (!pending_request)
break;
+ // Note that this post can race with other stream creations, and it's
+ // possible that the un-stalled stream will be stalled again if it loses.
+ // TODO(jgraettinger): Provide stronger ordering guarantees.
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&SpdySession::CompleteStreamRequest,
@@ -766,23 +929,46 @@ SpdyMajorVersion SpdySession::GetProtocolVersion() const {
return buffered_spdy_framer_->protocol_version();
}
+bool SpdySession::HasAcceptableTransportSecurity() const {
+ // If we're not even using TLS, we have no standards to meet.
+ if (!is_secure_) {
+ return true;
+ }
+
+ // We don't enforce transport security standards for older SPDY versions.
+ if (GetProtocolVersion() < SPDY4) {
+ return true;
+ }
+
+ SSLInfo ssl_info;
+ CHECK(connection_->socket()->GetSSLInfo(&ssl_info));
+
+ // HTTP/2 requires TLS 1.2+
+ if (SSLConnectionStatusToVersion(ssl_info.connection_status) <
+ SSL_CONNECTION_VERSION_TLS1_2) {
+ return false;
+ }
+
+ if (!IsSecureTLSCipherSuite(
+ SSLConnectionStatusToCipherSuite(ssl_info.connection_status))) {
+ return false;
+ }
+
+ return true;
+}
+
base::WeakPtr<SpdySession> SpdySession::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
bool SpdySession::CloseOneIdleConnection() {
CHECK(!in_io_loop_);
- DCHECK_NE(availability_state_, STATE_CLOSED);
DCHECK(pool_);
- if (!active_streams_.empty())
- return false;
- CloseSessionResult result =
- DoCloseSession(ERR_CONNECTION_CLOSED, "Closing one idle connection.");
- if (result != SESSION_CLOSED_AND_REMOVED) {
- NOTREACHED();
- return false;
+ if (active_streams_.empty()) {
+ DoDrainSession(ERR_CONNECTION_CLOSED, "Closing idle connection.");
}
- return true;
+ // Return false as the socket wasn't immediately closed.
+ return false;
}
void SpdySession::EnqueueStreamWrite(
@@ -799,7 +985,6 @@ void SpdySession::EnqueueStreamWrite(
scoped_ptr<SpdyFrame> SpdySession::CreateSynStream(
SpdyStreamId stream_id,
RequestPriority priority,
- uint8 credential_slot,
SpdyControlFlags flags,
const SpdyHeaderBlock& headers) {
ActiveStreamMap::const_iterator it = active_streams_.find(stream_id);
@@ -812,15 +997,14 @@ scoped_ptr<SpdyFrame> SpdySession::CreateSynStream(
SpdyPriority spdy_priority =
ConvertRequestPriorityToSpdyPriority(priority, GetProtocolVersion());
scoped_ptr<SpdyFrame> syn_frame(
- buffered_spdy_framer_->CreateSynStream(
- stream_id, 0, spdy_priority,
- credential_slot, flags, &headers));
+ buffered_spdy_framer_->CreateSynStream(stream_id, 0, spdy_priority, flags,
+ &headers));
base::StatsCounter spdy_requests("spdy.requests");
spdy_requests.Increment();
streams_initiated_count_++;
- if (net_log().IsLoggingAllEvents()) {
+ if (net_log().IsLogging()) {
net_log().AddEvent(
NetLog::TYPE_SPDY_SESSION_SYN_STREAM,
base::Bind(&NetLogSpdySynStreamSentCallback, &headers,
@@ -837,8 +1021,7 @@ scoped_ptr<SpdyBuffer> SpdySession::CreateDataBuffer(SpdyStreamId stream_id,
IOBuffer* data,
int len,
SpdyDataFlags flags) {
- if (availability_state_ == STATE_CLOSED) {
- NOTREACHED();
+ if (availability_state_ == STATE_DRAINING) {
return scoped_ptr<SpdyBuffer>();
}
@@ -929,7 +1112,7 @@ scoped_ptr<SpdyBuffer> SpdySession::CreateDataBuffer(SpdyStreamId stream_id,
if (effective_len < len)
flags = static_cast<SpdyDataFlags>(flags & ~DATA_FLAG_FIN);
- if (net_log().IsLoggingAllEvents()) {
+ if (net_log().IsLogging()) {
net_log().AddEvent(
NetLog::TYPE_SPDY_SESSION_SEND_DATA,
base::Bind(&NetLogSpdyDataCallback, stream_id, effective_len,
@@ -1027,22 +1210,13 @@ void SpdySession::CloseActiveStreamIterator(ActiveStreamMap::iterator it,
if (owned_stream->type() == SPDY_PUSH_STREAM)
unclaimed_pushed_streams_.erase(owned_stream->url());
- base::WeakPtr<SpdySession> weak_this = GetWeakPtr();
-
DeleteStream(owned_stream.Pass(), status);
-
- if (!weak_this)
- return;
-
- if (availability_state_ == STATE_CLOSED)
- return;
+ MaybeFinishGoingAway();
// If there are no active streams and the socket pool is stalled, close the
// session to free up a socket slot.
if (active_streams_.empty() && connection_->IsPoolStalled()) {
- CloseSessionResult result =
- DoCloseSession(ERR_CONNECTION_CLOSED, "Closing idle connection.");
- DCHECK_NE(result, SESSION_ALREADY_CLOSED);
+ DoDrainSession(ERR_CONNECTION_CLOSED, "Closing idle connection.");
}
}
@@ -1082,42 +1256,31 @@ void SpdySession::EnqueueResetStreamFrame(SpdyStreamId stream_id,
buffered_spdy_framer_->CreateRstStream(stream_id, status));
EnqueueSessionWrite(priority, RST_STREAM, rst_frame.Pass());
- RecordProtocolErrorHistogram(
- static_cast<SpdyProtocolErrorDetails>(status + STATUS_CODE_INVALID));
+ RecordProtocolErrorHistogram(MapRstStreamStatusToProtocolError(status));
}
void SpdySession::PumpReadLoop(ReadState expected_read_state, int result) {
CHECK(!in_io_loop_);
- DCHECK_NE(availability_state_, STATE_CLOSED);
- DCHECK_EQ(read_state_, expected_read_state);
-
- result = DoReadLoop(expected_read_state, result);
-
- if (availability_state_ == STATE_CLOSED) {
- DCHECK_EQ(result, error_on_close_);
- DCHECK_LT(error_on_close_, ERR_IO_PENDING);
- RemoveFromPool();
+ if (availability_state_ == STATE_DRAINING) {
return;
}
-
- DCHECK(result == OK || result == ERR_IO_PENDING);
+ ignore_result(DoReadLoop(expected_read_state, result));
}
int SpdySession::DoReadLoop(ReadState expected_read_state, int result) {
CHECK(!in_io_loop_);
- DCHECK_NE(availability_state_, STATE_CLOSED);
- DCHECK_EQ(read_state_, expected_read_state);
+ CHECK_EQ(read_state_, expected_read_state);
in_io_loop_ = true;
int bytes_read_without_yielding = 0;
- // Loop until the session is closed, the read becomes blocked, or
+ // Loop until the session is draining, the read becomes blocked, or
// the read limit is exceeded.
while (true) {
switch (read_state_) {
case READ_STATE_DO_READ:
- DCHECK_EQ(result, OK);
+ CHECK_EQ(result, OK);
result = DoRead();
break;
case READ_STATE_DO_READ_COMPLETE:
@@ -1130,11 +1293,8 @@ int SpdySession::DoReadLoop(ReadState expected_read_state, int result) {
break;
}
- if (availability_state_ == STATE_CLOSED) {
- DCHECK_EQ(result, error_on_close_);
- DCHECK_LT(result, ERR_IO_PENDING);
+ if (availability_state_ == STATE_DRAINING)
break;
- }
if (result == ERR_IO_PENDING)
break;
@@ -1158,7 +1318,6 @@ int SpdySession::DoReadLoop(ReadState expected_read_state, int result) {
int SpdySession::DoRead() {
CHECK(in_io_loop_);
- DCHECK_NE(availability_state_, STATE_CLOSED);
CHECK(connection_);
CHECK(connection_->socket());
@@ -1172,7 +1331,6 @@ int SpdySession::DoRead() {
int SpdySession::DoReadComplete(int result) {
CHECK(in_io_loop_);
- DCHECK_NE(availability_state_, STATE_CLOSED);
// Parse a frame. For now this code requires that the frame fit into our
// buffer (kReadBufferSize).
@@ -1181,23 +1339,16 @@ int SpdySession::DoReadComplete(int result) {
if (result == 0) {
UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdySession.BytesRead.EOF",
total_bytes_received_, 1, 100000000, 50);
- CloseSessionResult close_session_result =
- DoCloseSession(ERR_CONNECTION_CLOSED, "Connection closed");
- DCHECK_EQ(close_session_result, SESSION_CLOSED_BUT_NOT_REMOVED);
- DCHECK_EQ(availability_state_, STATE_CLOSED);
- DCHECK_EQ(error_on_close_, ERR_CONNECTION_CLOSED);
+ DoDrainSession(ERR_CONNECTION_CLOSED, "Connection closed");
+
return ERR_CONNECTION_CLOSED;
}
if (result < 0) {
- CloseSessionResult close_session_result =
- DoCloseSession(static_cast<Error>(result), "result is < 0.");
- DCHECK_EQ(close_session_result, SESSION_CLOSED_BUT_NOT_REMOVED);
- DCHECK_EQ(availability_state_, STATE_CLOSED);
- DCHECK_EQ(error_on_close_, result);
+ DoDrainSession(static_cast<Error>(result), "result is < 0.");
return result;
}
-
+ CHECK_LE(result, kReadBufferSize);
total_bytes_received_ += result;
last_activity_time_ = time_func_();
@@ -1209,9 +1360,8 @@ int SpdySession::DoReadComplete(int result) {
result -= bytes_processed;
data += bytes_processed;
- if (availability_state_ == STATE_CLOSED) {
- DCHECK_LT(error_on_close_, ERR_IO_PENDING);
- return error_on_close_;
+ if (availability_state_ == STATE_DRAINING) {
+ return ERR_CONNECTION_CLOSED;
}
DCHECK_EQ(buffered_spdy_framer_->error_code(), SpdyFramer::SPDY_NO_ERROR);
@@ -1223,24 +1373,19 @@ int SpdySession::DoReadComplete(int result) {
void SpdySession::PumpWriteLoop(WriteState expected_write_state, int result) {
CHECK(!in_io_loop_);
- DCHECK_NE(availability_state_, STATE_CLOSED);
DCHECK_EQ(write_state_, expected_write_state);
- result = DoWriteLoop(expected_write_state, result);
+ DoWriteLoop(expected_write_state, result);
- if (availability_state_ == STATE_CLOSED) {
- DCHECK_EQ(result, error_on_close_);
- DCHECK_LT(error_on_close_, ERR_IO_PENDING);
- RemoveFromPool();
+ if (availability_state_ == STATE_DRAINING && !in_flight_write_ &&
+ write_queue_.IsEmpty()) {
+ pool_->RemoveUnavailableSession(GetWeakPtr()); // Destroys |this|.
return;
}
-
- DCHECK(result == OK || result == ERR_IO_PENDING);
}
int SpdySession::DoWriteLoop(WriteState expected_write_state, int result) {
CHECK(!in_io_loop_);
- DCHECK_NE(availability_state_, STATE_CLOSED);
DCHECK_NE(write_state_, WRITE_STATE_IDLE);
DCHECK_EQ(write_state_, expected_write_state);
@@ -1262,12 +1407,6 @@ int SpdySession::DoWriteLoop(WriteState expected_write_state, int result) {
break;
}
- if (availability_state_ == STATE_CLOSED) {
- DCHECK_EQ(result, error_on_close_);
- DCHECK_LT(result, ERR_IO_PENDING);
- break;
- }
-
if (write_state_ == WRITE_STATE_IDLE) {
DCHECK_EQ(result, ERR_IO_PENDING);
break;
@@ -1285,7 +1424,6 @@ int SpdySession::DoWriteLoop(WriteState expected_write_state, int result) {
int SpdySession::DoWrite() {
CHECK(in_io_loop_);
- DCHECK_NE(availability_state_, STATE_CLOSED);
DCHECK(buffered_spdy_framer_);
if (in_flight_write_) {
@@ -1301,18 +1439,23 @@ int SpdySession::DoWrite() {
}
if (stream.get())
- DCHECK(!stream->IsClosed());
+ CHECK(!stream->IsClosed());
// Activate the stream only when sending the SYN_STREAM frame to
// guarantee monotonically-increasing stream IDs.
if (frame_type == SYN_STREAM) {
- if (stream.get() && stream->stream_id() == 0) {
- scoped_ptr<SpdyStream> owned_stream =
- ActivateCreatedStream(stream.get());
- InsertActivatedStream(owned_stream.Pass());
- } else {
- NOTREACHED();
- return ERR_UNEXPECTED;
+ CHECK(stream.get());
+ CHECK_EQ(stream->stream_id(), 0u);
+ scoped_ptr<SpdyStream> owned_stream =
+ ActivateCreatedStream(stream.get());
+ InsertActivatedStream(owned_stream.Pass());
+
+ if (stream_hi_water_mark_ > kLastStreamId) {
+ CHECK_EQ(stream->stream_id(), kLastStreamId);
+ // We've exhausted the stream ID space, and no new streams may be
+ // created after this one.
+ MakeUnavailable();
+ StartGoingAway(kLastStreamId, ERR_ABORTED);
}
}
@@ -1344,7 +1487,6 @@ int SpdySession::DoWrite() {
int SpdySession::DoWriteComplete(int result) {
CHECK(in_io_loop_);
- DCHECK_NE(availability_state_, STATE_CLOSED);
DCHECK_NE(result, ERR_IO_PENDING);
DCHECK_GT(in_flight_write_->GetRemainingSize(), 0u);
@@ -1356,12 +1498,9 @@ int SpdySession::DoWriteComplete(int result) {
in_flight_write_frame_type_ = DATA;
in_flight_write_frame_size_ = 0;
in_flight_write_stream_.reset();
- CloseSessionResult close_session_result =
- DoCloseSession(static_cast<Error>(result), "Write error");
- DCHECK_EQ(close_session_result, SESSION_CLOSED_BUT_NOT_REMOVED);
- DCHECK_EQ(availability_state_, STATE_CLOSED);
- DCHECK_EQ(error_on_close_, result);
- return result;
+ write_state_ = WRITE_STATE_DO_WRITE;
+ DoDrainSession(static_cast<Error>(result), "Write error");
+ return OK;
}
// It should not be possible to have written more bytes than our
@@ -1396,22 +1535,20 @@ int SpdySession::DoWriteComplete(int result) {
}
void SpdySession::DcheckGoingAway() const {
+#if DCHECK_IS_ON
DCHECK_GE(availability_state_, STATE_GOING_AWAY);
- if (DCHECK_IS_ON()) {
- for (int i = MINIMUM_PRIORITY; i <= MAXIMUM_PRIORITY; ++i) {
- DCHECK(pending_create_stream_queues_[i].empty());
- }
+ for (int i = MINIMUM_PRIORITY; i <= MAXIMUM_PRIORITY; ++i) {
+ DCHECK(pending_create_stream_queues_[i].empty());
}
DCHECK(created_streams_.empty());
+#endif
}
-void SpdySession::DcheckClosed() const {
+void SpdySession::DcheckDraining() const {
DcheckGoingAway();
- DCHECK_EQ(availability_state_, STATE_CLOSED);
- DCHECK_LT(error_on_close_, ERR_IO_PENDING);
+ DCHECK_EQ(availability_state_, STATE_DRAINING);
DCHECK(active_streams_.empty());
DCHECK(unclaimed_pushed_streams_.empty());
- DCHECK(write_queue_.IsEmpty());
}
void SpdySession::StartGoingAway(SpdyStreamId last_good_stream_id,
@@ -1461,21 +1598,39 @@ void SpdySession::StartGoingAway(SpdyStreamId last_good_stream_id,
}
void SpdySession::MaybeFinishGoingAway() {
- DcheckGoingAway();
- if (active_streams_.empty() && availability_state_ != STATE_CLOSED) {
- CloseSessionResult result =
- DoCloseSession(ERR_CONNECTION_CLOSED, "Finished going away");
- DCHECK_NE(result, SESSION_ALREADY_CLOSED);
+ if (active_streams_.empty() && availability_state_ == STATE_GOING_AWAY) {
+ DoDrainSession(OK, "Finished going away");
}
}
-SpdySession::CloseSessionResult SpdySession::DoCloseSession(
- Error err,
- const std::string& description) {
- DCHECK_LT(err, ERR_IO_PENDING);
-
- if (availability_state_ == STATE_CLOSED)
- return SESSION_ALREADY_CLOSED;
+void SpdySession::DoDrainSession(Error err, const std::string& description) {
+ if (availability_state_ == STATE_DRAINING) {
+ return;
+ }
+ MakeUnavailable();
+
+ // If |err| indicates an error occurred, inform the peer that we're closing
+ // and why. Don't GOAWAY on a graceful or idle close, as that may
+ // unnecessarily wake the radio. We could technically GOAWAY on network errors
+ // (we'll probably fail to actually write it, but that's okay), however many
+ // unit-tests would need to be updated.
+ if (err != OK &&
+ err != ERR_ABORTED && // Used by SpdySessionPool to close idle sessions.
+ err != ERR_NETWORK_CHANGED && // Used to deprecate sessions on IP change.
+ err != ERR_SOCKET_NOT_CONNECTED &&
+ err != ERR_CONNECTION_CLOSED && err != ERR_CONNECTION_RESET) {
+ // Enqueue a GOAWAY to inform the peer of why we're closing the connection.
+ SpdyGoAwayIR goaway_ir(0, // Last accepted stream ID.
+ MapNetErrorToGoAwayStatus(err),
+ description);
+ EnqueueSessionWrite(HIGHEST,
+ GOAWAY,
+ scoped_ptr<SpdyFrame>(
+ buffered_spdy_framer_->SerializeFrame(goaway_ir)));
+ }
+
+ availability_state_ = STATE_DRAINING;
+ error_on_close_ = err;
net_log_.AddEvent(
NetLog::TYPE_SPDY_SESSION_CLOSE,
@@ -1485,33 +1640,14 @@ SpdySession::CloseSessionResult SpdySession::DoCloseSession(
UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdySession.BytesRead.OtherErrors",
total_bytes_received_, 1, 100000000, 50);
- // |pool_| will be NULL when |InitializeWithSocket()| is in the
- // call stack.
- if (pool_ && availability_state_ != STATE_GOING_AWAY)
- pool_->MakeSessionUnavailable(GetWeakPtr());
-
- availability_state_ = STATE_CLOSED;
- error_on_close_ = err;
-
- StartGoingAway(0, err);
- write_queue_.Clear();
-
- DcheckClosed();
-
- if (in_io_loop_)
- return SESSION_CLOSED_BUT_NOT_REMOVED;
-
- RemoveFromPool();
- return SESSION_CLOSED_AND_REMOVED;
-}
-
-void SpdySession::RemoveFromPool() {
- DcheckClosed();
- CHECK(pool_);
-
- SpdySessionPool* pool = pool_;
- pool_ = NULL;
- pool->RemoveUnavailableSession(GetWeakPtr());
+ if (err == OK) {
+ // We ought to be going away already, as this is a graceful close.
+ DcheckGoingAway();
+ } else {
+ StartGoingAway(0, err);
+ }
+ DcheckDraining();
+ MaybePostWriteLoop();
}
void SpdySession::LogAbandonedStream(SpdyStream* stream, Error status) {
@@ -1541,19 +1677,24 @@ void SpdySession::LogAbandonedActiveStream(ActiveStreamMap::const_iterator it,
}
}
-int SpdySession::GetNewStreamId() {
- int id = stream_hi_water_mark_;
+SpdyStreamId SpdySession::GetNewStreamId() {
+ CHECK_LE(stream_hi_water_mark_, kLastStreamId);
+ SpdyStreamId id = stream_hi_water_mark_;
stream_hi_water_mark_ += 2;
- if (stream_hi_water_mark_ > 0x7fff)
- stream_hi_water_mark_ = 1;
return id;
}
void SpdySession::CloseSessionOnError(Error err,
const std::string& description) {
- // We may be called from anywhere, so we can't expect a particular
- // return value.
- ignore_result(DoCloseSession(err, description));
+ DCHECK_LT(err, ERR_IO_PENDING);
+ DoDrainSession(err, description);
+}
+
+void SpdySession::MakeUnavailable() {
+ if (availability_state_ == STATE_AVAILABLE) {
+ availability_state_ = STATE_GOING_AWAY;
+ pool_->MakeSessionUnavailable(GetWeakPtr());
+ }
}
base::Value* SpdySession::GetInfoAsValue() const {
@@ -1607,7 +1748,8 @@ base::Value* SpdySession::GetInfoAsValue() const {
}
bool SpdySession::IsReused() const {
- return buffered_spdy_framer_->frames_received() > 0;
+ return buffered_spdy_framer_->frames_received() > 0 ||
+ connection_->reuse_type() == ClientSocketHandle::UNUSED_IDLE;
}
bool SpdySession::GetLoadTimingInfo(SpdyStreamId stream_id,
@@ -1643,10 +1785,9 @@ int SpdySession::GetLocalAddress(IPEndPoint* address) const {
void SpdySession::EnqueueSessionWrite(RequestPriority priority,
SpdyFrameType frame_type,
scoped_ptr<SpdyFrame> frame) {
- DCHECK(frame_type == RST_STREAM ||
- frame_type == SETTINGS ||
- frame_type == WINDOW_UPDATE ||
- frame_type == PING);
+ DCHECK(frame_type == RST_STREAM || frame_type == SETTINGS ||
+ frame_type == WINDOW_UPDATE || frame_type == PING ||
+ frame_type == GOAWAY);
EnqueueWrite(
priority, frame_type,
scoped_ptr<SpdyBufferProducer>(
@@ -1659,14 +1800,16 @@ void SpdySession::EnqueueWrite(RequestPriority priority,
SpdyFrameType frame_type,
scoped_ptr<SpdyBufferProducer> producer,
const base::WeakPtr<SpdyStream>& stream) {
- if (availability_state_ == STATE_CLOSED)
+ if (availability_state_ == STATE_DRAINING)
return;
- bool was_idle = write_queue_.IsEmpty();
write_queue_.Enqueue(priority, frame_type, producer.Pass(), stream);
+ MaybePostWriteLoop();
+}
+
+void SpdySession::MaybePostWriteLoop() {
if (write_state_ == WRITE_STATE_IDLE) {
- DCHECK(was_idle);
- DCHECK(!in_flight_write_);
+ CHECK(!in_flight_write_);
write_state_ = WRITE_STATE_DO_WRITE;
base::MessageLoop::current()->PostTask(
FROM_HERE,
@@ -1676,14 +1819,14 @@ void SpdySession::EnqueueWrite(RequestPriority priority,
}
void SpdySession::InsertCreatedStream(scoped_ptr<SpdyStream> stream) {
- DCHECK_EQ(stream->stream_id(), 0u);
- DCHECK(created_streams_.find(stream.get()) == created_streams_.end());
+ CHECK_EQ(stream->stream_id(), 0u);
+ CHECK(created_streams_.find(stream.get()) == created_streams_.end());
created_streams_.insert(stream.release());
}
scoped_ptr<SpdyStream> SpdySession::ActivateCreatedStream(SpdyStream* stream) {
- DCHECK_EQ(stream->stream_id(), 0u);
- DCHECK(created_streams_.find(stream) != created_streams_.end());
+ CHECK_EQ(stream->stream_id(), 0u);
+ CHECK(created_streams_.find(stream) != created_streams_.end());
stream->set_stream_id(GetNewStreamId());
scoped_ptr<SpdyStream> owned_stream(stream);
created_streams_.erase(stream);
@@ -1692,15 +1835,12 @@ scoped_ptr<SpdyStream> SpdySession::ActivateCreatedStream(SpdyStream* stream) {
void SpdySession::InsertActivatedStream(scoped_ptr<SpdyStream> stream) {
SpdyStreamId stream_id = stream->stream_id();
- DCHECK_NE(stream_id, 0u);
+ CHECK_NE(stream_id, 0u);
std::pair<ActiveStreamMap::iterator, bool> result =
active_streams_.insert(
std::make_pair(stream_id, ActiveStreamInfo(stream.get())));
- if (result.second) {
- ignore_result(stream.release());
- } else {
- NOTREACHED();
- }
+ CHECK(result.second);
+ ignore_result(stream.release());
}
void SpdySession::DeleteStream(scoped_ptr<SpdyStream> stream, int status) {
@@ -1713,26 +1853,10 @@ void SpdySession::DeleteStream(scoped_ptr<SpdyStream> stream, int status) {
}
write_queue_.RemovePendingWritesForStream(stream->GetWeakPtr());
-
- // |stream->OnClose()| may end up closing |this|, so detect that.
- base::WeakPtr<SpdySession> weak_this = GetWeakPtr();
-
stream->OnClose(status);
- if (!weak_this)
- return;
-
- switch (availability_state_) {
- case STATE_AVAILABLE:
- ProcessPendingStreamRequests();
- break;
- case STATE_GOING_AWAY:
- DcheckGoingAway();
- MaybeFinishGoingAway();
- break;
- case STATE_CLOSED:
- // Do nothing.
- break;
+ if (availability_state_ == STATE_AVAILABLE) {
+ ProcessPendingStreamRequests();
}
}
@@ -1776,25 +1900,18 @@ bool SpdySession::GetSSLCertRequestInfo(
void SpdySession::OnError(SpdyFramer::SpdyError error_code) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
- return;
-
- RecordProtocolErrorHistogram(
- static_cast<SpdyProtocolErrorDetails>(error_code));
- std::string description = base::StringPrintf(
- "SPDY_ERROR error_code: %d.", error_code);
- CloseSessionResult result =
- DoCloseSession(ERR_SPDY_PROTOCOL_ERROR, description);
- DCHECK_EQ(result, SESSION_CLOSED_BUT_NOT_REMOVED);
+ RecordProtocolErrorHistogram(MapFramerErrorToProtocolError(error_code));
+ std::string description =
+ base::StringPrintf("Framer error: %d (%s).",
+ error_code,
+ SpdyFramer::ErrorCodeToString(error_code));
+ DoDrainSession(MapFramerErrorToNetError(error_code), description);
}
void SpdySession::OnStreamError(SpdyStreamId stream_id,
const std::string& description) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
- return;
-
ActiveStreamMap::iterator it = active_streams_.find(stream_id);
if (it == active_streams_.end()) {
// We still want to send a frame to reset the stream even if we
@@ -1812,9 +1929,6 @@ void SpdySession::OnDataFrameHeader(SpdyStreamId stream_id,
bool fin) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
- return;
-
ActiveStreamMap::iterator it = active_streams_.find(stream_id);
// By the time data comes in, the stream may already be inactive.
@@ -1835,11 +1949,15 @@ void SpdySession::OnStreamFrameData(SpdyStreamId stream_id,
bool fin) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
+ if (data == NULL && len != 0) {
+ // This is notification of consumed data padding.
+ // TODO(jgraettinger): Properly flow padding into WINDOW_UPDATE frames.
+ // See crbug.com/353012.
return;
+ }
DCHECK_LT(len, 1u << 24);
- if (net_log().IsLoggingAllEvents()) {
+ if (net_log().IsLogging()) {
net_log().AddEvent(
NetLog::TYPE_SPDY_SESSION_RECV_DATA,
base::Bind(&NetLogSpdyDataCallback, stream_id, len, fin));
@@ -1853,6 +1971,7 @@ void SpdySession::OnStreamFrameData(SpdyStreamId stream_id,
scoped_ptr<SpdyBuffer> buffer;
if (data) {
DCHECK_GT(len, 0u);
+ CHECK_LE(len, static_cast<size_t>(kReadBufferSize));
buffer.reset(new SpdyBuffer(data, len));
if (flow_control_state_ == FLOW_CONTROL_STREAM_AND_SESSION) {
@@ -1889,18 +2008,26 @@ void SpdySession::OnStreamFrameData(SpdyStreamId stream_id,
void SpdySession::OnSettings(bool clear_persisted) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
- return;
-
if (clear_persisted)
http_server_properties_->ClearSpdySettings(host_port_pair());
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.AddEvent(
NetLog::TYPE_SPDY_SESSION_RECV_SETTINGS,
base::Bind(&NetLogSpdySettingsCallback, host_port_pair(),
clear_persisted));
}
+
+ if (GetProtocolVersion() >= SPDY4) {
+ // Send an acknowledgment of the setting.
+ SpdySettingsIR settings_ir;
+ settings_ir.set_is_ack(true);
+ EnqueueSessionWrite(
+ HIGHEST,
+ SETTINGS,
+ scoped_ptr<SpdyFrame>(
+ buffered_spdy_framer_->SerializeFrame(settings_ir)));
+ }
}
void SpdySession::OnSetting(SpdySettingsIds id,
@@ -1908,9 +2035,6 @@ void SpdySession::OnSetting(SpdySettingsIds id,
uint32 value) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
- return;
-
HandleSetting(id, value);
http_server_properties_->SetSpdySetting(
host_port_pair(),
@@ -1973,19 +2097,21 @@ int SpdySession::OnInitialResponseHeadersReceived(
void SpdySession::OnSynStream(SpdyStreamId stream_id,
SpdyStreamId associated_stream_id,
SpdyPriority priority,
- uint8 credential_slot,
bool fin,
bool unidirectional,
const SpdyHeaderBlock& headers) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
+ if (GetProtocolVersion() >= SPDY4) {
+ DCHECK_EQ(0u, associated_stream_id);
+ OnHeaders(stream_id, fin, headers);
return;
+ }
base::Time response_time = base::Time::Now();
base::TimeTicks recv_first_byte_time = time_func_();
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.AddEvent(
NetLog::TYPE_SPDY_SESSION_PUSHED_SYN_STREAM,
base::Bind(&NetLogSpdySynStreamReceivedCallback,
@@ -1993,117 +2119,15 @@ void SpdySession::OnSynStream(SpdyStreamId stream_id,
stream_id, associated_stream_id));
}
- // Server-initiated streams should have even sequence numbers.
- if ((stream_id & 0x1) != 0) {
- LOG(WARNING) << "Received invalid OnSyn stream id " << stream_id;
- return;
- }
-
- if (IsStreamActive(stream_id)) {
- LOG(WARNING) << "Received OnSyn for active stream " << stream_id;
- return;
- }
-
- RequestPriority request_priority =
- ConvertSpdyPriorityToRequestPriority(priority, GetProtocolVersion());
-
- if (availability_state_ == STATE_GOING_AWAY) {
- // TODO(akalin): This behavior isn't in the SPDY spec, although it
- // probably should be.
- EnqueueResetStreamFrame(stream_id, request_priority,
- RST_STREAM_REFUSED_STREAM,
- "OnSyn received when going away");
- return;
- }
-
- if (associated_stream_id == 0) {
- std::string description = base::StringPrintf(
- "Received invalid OnSyn associated stream id %d for stream %d",
- associated_stream_id, stream_id);
- EnqueueResetStreamFrame(stream_id, request_priority,
- RST_STREAM_REFUSED_STREAM, description);
- return;
- }
-
- streams_pushed_count_++;
-
- // TODO(mbelshe): DCHECK that this is a GET method?
-
- // Verify that the response had a URL for us.
- GURL gurl = GetUrlFromHeaderBlock(headers, GetProtocolVersion(), true);
- if (!gurl.is_valid()) {
- EnqueueResetStreamFrame(
- stream_id, request_priority, RST_STREAM_PROTOCOL_ERROR,
- "Pushed stream url was invalid: " + gurl.spec());
- return;
- }
-
- // Verify we have a valid stream association.
- ActiveStreamMap::iterator associated_it =
- active_streams_.find(associated_stream_id);
- if (associated_it == active_streams_.end()) {
- EnqueueResetStreamFrame(
- stream_id, request_priority, RST_STREAM_INVALID_STREAM,
- base::StringPrintf(
- "Received OnSyn with inactive associated stream %d",
- associated_stream_id));
- return;
- }
-
- // Check that the SYN advertises the same origin as its associated stream.
- // Bypass this check if and only if this session is with a SPDY proxy that
- // is trusted explicitly via the --trusted-spdy-proxy switch.
- if (trusted_spdy_proxy_.Equals(host_port_pair())) {
- // Disallow pushing of HTTPS content.
- if (gurl.SchemeIs("https")) {
- EnqueueResetStreamFrame(
- stream_id, request_priority, RST_STREAM_REFUSED_STREAM,
- base::StringPrintf(
- "Rejected push of Cross Origin HTTPS content %d",
- associated_stream_id));
- }
- } else {
- GURL associated_url(associated_it->second.stream->GetUrlFromHeaders());
- if (associated_url.GetOrigin() != gurl.GetOrigin()) {
- EnqueueResetStreamFrame(
- stream_id, request_priority, RST_STREAM_REFUSED_STREAM,
- base::StringPrintf(
- "Rejected Cross Origin Push Stream %d",
- associated_stream_id));
- return;
- }
- }
+ // Split headers to simulate push promise and response.
+ SpdyHeaderBlock request_headers;
+ SpdyHeaderBlock response_headers;
+ SplitPushedHeadersToRequestAndResponse(
+ headers, GetProtocolVersion(), &request_headers, &response_headers);
- // There should not be an existing pushed stream with the same path.
- PushedStreamMap::iterator pushed_it =
- unclaimed_pushed_streams_.lower_bound(gurl);
- if (pushed_it != unclaimed_pushed_streams_.end() &&
- pushed_it->first == gurl) {
- EnqueueResetStreamFrame(
- stream_id, request_priority, RST_STREAM_PROTOCOL_ERROR,
- "Received duplicate pushed stream with url: " +
- gurl.spec());
+ if (!TryCreatePushStream(
+ stream_id, associated_stream_id, priority, request_headers))
return;
- }
-
- scoped_ptr<SpdyStream> stream(
- new SpdyStream(SPDY_PUSH_STREAM, GetWeakPtr(), gurl,
- request_priority,
- stream_initial_send_window_size_,
- stream_initial_recv_window_size_,
- net_log_));
- stream->set_stream_id(stream_id);
- stream->IncrementRawReceivedBytes(last_compressed_frame_len_);
- last_compressed_frame_len_ = 0;
-
- DeleteExpiredPushedStreams();
- PushedStreamMap::iterator inserted_pushed_it =
- unclaimed_pushed_streams_.insert(
- pushed_it,
- std::make_pair(gurl, PushedStreamInfo(stream_id, time_func_())));
- DCHECK(inserted_pushed_it != pushed_it);
-
- InsertActivatedStream(stream.Pass());
ActiveStreamMap::iterator active_it = active_streams_.find(stream_id);
if (active_it == active_streams_.end()) {
@@ -2111,10 +2135,10 @@ void SpdySession::OnSynStream(SpdyStreamId stream_id,
return;
}
- // Parse the headers.
- if (OnInitialResponseHeadersReceived(
- headers, response_time,
- recv_first_byte_time, active_it->second.stream) != OK)
+ if (OnInitialResponseHeadersReceived(response_headers,
+ response_time,
+ recv_first_byte_time,
+ active_it->second.stream) != OK)
return;
base::StatsCounter push_requests("spdy.pushed_streams");
@@ -2149,7 +2173,8 @@ void SpdySession::DeleteExpiredPushedStreams() {
LogAbandonedActiveStream(active_it, ERR_INVALID_SPDY_STREAM);
// CloseActiveStreamIterator() will remove the stream from
// |unclaimed_pushed_streams_|.
- CloseActiveStreamIterator(active_it, ERR_INVALID_SPDY_STREAM);
+ ResetStreamIterator(
+ active_it, RST_STREAM_REFUSED_STREAM, "Stream not claimed.");
}
next_unclaimed_push_stream_sweep_time_ = time_func_() +
@@ -2161,13 +2186,10 @@ void SpdySession::OnSynReply(SpdyStreamId stream_id,
const SpdyHeaderBlock& headers) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
- return;
-
base::Time response_time = base::Time::Now();
base::TimeTicks recv_first_byte_time = time_func_();
- if (net_log().IsLoggingAllEvents()) {
+ if (net_log().IsLogging()) {
net_log().AddEvent(
NetLog::TYPE_SPDY_SESSION_SYN_REPLY,
base::Bind(&NetLogSpdySynReplyOrHeadersReceivedCallback,
@@ -2186,11 +2208,18 @@ void SpdySession::OnSynReply(SpdyStreamId stream_id,
stream->IncrementRawReceivedBytes(last_compressed_frame_len_);
last_compressed_frame_len_ = 0;
+ if (GetProtocolVersion() >= SPDY4) {
+ const std::string& error =
+ "SPDY4 wasn't expecting SYN_REPLY.";
+ stream->LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error);
+ ResetStreamIterator(it, RST_STREAM_PROTOCOL_ERROR, error);
+ return;
+ }
if (!it->second.waiting_for_syn_reply) {
const std::string& error =
"Received duplicate SYN_REPLY for stream.";
stream->LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error);
- ResetStreamIterator(it, RST_STREAM_STREAM_IN_USE, error);
+ ResetStreamIterator(it, RST_STREAM_PROTOCOL_ERROR, error);
return;
}
it->second.waiting_for_syn_reply = false;
@@ -2204,10 +2233,7 @@ void SpdySession::OnHeaders(SpdyStreamId stream_id,
const SpdyHeaderBlock& headers) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
- return;
-
- if (net_log().IsLoggingAllEvents()) {
+ if (net_log().IsLogging()) {
net_log().AddEvent(
NetLog::TYPE_SPDY_SESSION_RECV_HEADERS,
base::Bind(&NetLogSpdySynReplyOrHeadersReceivedCallback,
@@ -2227,10 +2253,30 @@ void SpdySession::OnHeaders(SpdyStreamId stream_id,
stream->IncrementRawReceivedBytes(last_compressed_frame_len_);
last_compressed_frame_len_ = 0;
- int rv = stream->OnAdditionalResponseHeadersReceived(headers);
- if (rv < 0) {
- DCHECK_NE(rv, ERR_IO_PENDING);
- DCHECK(active_streams_.find(stream_id) == active_streams_.end());
+ base::Time response_time = base::Time::Now();
+ base::TimeTicks recv_first_byte_time = time_func_();
+
+ if (it->second.waiting_for_syn_reply) {
+ if (GetProtocolVersion() < SPDY4) {
+ const std::string& error =
+ "Was expecting SYN_REPLY, not HEADERS.";
+ stream->LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error);
+ ResetStreamIterator(it, RST_STREAM_PROTOCOL_ERROR, error);
+ return;
+ }
+
+ it->second.waiting_for_syn_reply = false;
+ ignore_result(OnInitialResponseHeadersReceived(
+ headers, response_time, recv_first_byte_time, stream));
+ } else if (it->second.stream->IsReservedRemote()) {
+ ignore_result(OnInitialResponseHeadersReceived(
+ headers, response_time, recv_first_byte_time, stream));
+ } else {
+ int rv = stream->OnAdditionalResponseHeadersReceived(headers);
+ if (rv < 0) {
+ DCHECK_NE(rv, ERR_IO_PENDING);
+ DCHECK(active_streams_.find(stream_id) == active_streams_.end());
+ }
}
}
@@ -2238,9 +2284,6 @@ void SpdySession::OnRstStream(SpdyStreamId stream_id,
SpdyRstStreamStatus status) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
- return;
-
std::string description;
net_log().AddEvent(
NetLog::TYPE_SPDY_SESSION_RST_STREAM,
@@ -2276,8 +2319,7 @@ void SpdySession::OnGoAway(SpdyStreamId last_accepted_stream_id,
SpdyGoAwayStatus status) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
- return;
+ // TODO(jgraettinger): UMA histogram on |status|.
net_log_.AddEvent(NetLog::TYPE_SPDY_SESSION_GOAWAY,
base::Bind(&NetLogSpdyGoAwayCallback,
@@ -2285,13 +2327,7 @@ void SpdySession::OnGoAway(SpdyStreamId last_accepted_stream_id,
active_streams_.size(),
unclaimed_pushed_streams_.size(),
status));
- if (availability_state_ < STATE_GOING_AWAY) {
- availability_state_ = STATE_GOING_AWAY;
- // |pool_| will be NULL when |InitializeWithSocket()| is in the
- // call stack.
- if (pool_)
- pool_->MakeSessionUnavailable(GetWeakPtr());
- }
+ MakeUnavailable();
StartGoingAway(last_accepted_stream_id, ERR_ABORTED);
// This is to handle the case when we already don't have any active
// streams (i.e., StartGoingAway() did nothing). Otherwise, we have
@@ -2300,28 +2336,24 @@ void SpdySession::OnGoAway(SpdyStreamId last_accepted_stream_id,
MaybeFinishGoingAway();
}
-void SpdySession::OnPing(uint32 unique_id) {
+void SpdySession::OnPing(SpdyPingId unique_id, bool is_ack) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
- return;
-
net_log_.AddEvent(
NetLog::TYPE_SPDY_SESSION_PING,
- base::Bind(&NetLogSpdyPingCallback, unique_id, "received"));
+ base::Bind(&NetLogSpdyPingCallback, unique_id, is_ack, "received"));
// Send response to a PING from server.
- if (unique_id % 2 == 0) {
- WritePingFrame(unique_id);
+ if ((protocol_ >= kProtoSPDY4 && !is_ack) ||
+ (protocol_ < kProtoSPDY4 && unique_id % 2 == 0)) {
+ WritePingFrame(unique_id, true);
return;
}
--pings_in_flight_;
if (pings_in_flight_ < 0) {
RecordProtocolErrorHistogram(PROTOCOL_ERROR_UNEXPECTED_PING);
- CloseSessionResult result =
- DoCloseSession(ERR_SPDY_PROTOCOL_ERROR, "pings_in_flight_ is < 0.");
- DCHECK_EQ(result, SESSION_CLOSED_BUT_NOT_REMOVED);
+ DoDrainSession(ERR_SPDY_PROTOCOL_ERROR, "pings_in_flight_ is < 0.");
pings_in_flight_ = 0;
return;
}
@@ -2338,9 +2370,6 @@ void SpdySession::OnWindowUpdate(SpdyStreamId stream_id,
uint32 delta_window_size) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
- return;
-
DCHECK_LE(delta_window_size, static_cast<uint32>(kint32max));
net_log_.AddEvent(
NetLog::TYPE_SPDY_SESSION_RECEIVED_WINDOW_UPDATE_FRAME,
@@ -2358,11 +2387,10 @@ void SpdySession::OnWindowUpdate(SpdyStreamId stream_id,
if (delta_window_size < 1u) {
RecordProtocolErrorHistogram(PROTOCOL_ERROR_INVALID_WINDOW_UPDATE_SIZE);
- CloseSessionResult result = DoCloseSession(
+ DoDrainSession(
ERR_SPDY_PROTOCOL_ERROR,
"Received WINDOW_UPDATE with an invalid delta_window_size " +
- base::UintToString(delta_window_size));
- DCHECK_EQ(result, SESSION_CLOSED_BUT_NOT_REMOVED);
+ base::UintToString(delta_window_size));
return;
}
@@ -2402,9 +2430,172 @@ void SpdySession::OnWindowUpdate(SpdyStreamId stream_id,
}
}
+bool SpdySession::TryCreatePushStream(SpdyStreamId stream_id,
+ SpdyStreamId associated_stream_id,
+ SpdyPriority priority,
+ const SpdyHeaderBlock& headers) {
+ // Server-initiated streams should have even sequence numbers.
+ if ((stream_id & 0x1) != 0) {
+ LOG(WARNING) << "Received invalid push stream id " << stream_id;
+ return false;
+ }
+
+ if (IsStreamActive(stream_id)) {
+ LOG(WARNING) << "Received push for active stream " << stream_id;
+ return false;
+ }
+
+ RequestPriority request_priority =
+ ConvertSpdyPriorityToRequestPriority(priority, GetProtocolVersion());
+
+ if (availability_state_ == STATE_GOING_AWAY) {
+ // TODO(akalin): This behavior isn't in the SPDY spec, although it
+ // probably should be.
+ EnqueueResetStreamFrame(stream_id,
+ request_priority,
+ RST_STREAM_REFUSED_STREAM,
+ "push stream request received when going away");
+ return false;
+ }
+
+ if (associated_stream_id == 0) {
+ // In SPDY4 0 stream id in PUSH_PROMISE frame leads to framer error and
+ // session going away. We should never get here.
+ CHECK_GT(SPDY4, GetProtocolVersion());
+ std::string description = base::StringPrintf(
+ "Received invalid associated stream id %d for pushed stream %d",
+ associated_stream_id,
+ stream_id);
+ EnqueueResetStreamFrame(
+ stream_id, request_priority, RST_STREAM_REFUSED_STREAM, description);
+ return false;
+ }
+
+ streams_pushed_count_++;
+
+ // TODO(mbelshe): DCHECK that this is a GET method?
+
+ // Verify that the response had a URL for us.
+ GURL gurl = GetUrlFromHeaderBlock(headers, GetProtocolVersion(), true);
+ if (!gurl.is_valid()) {
+ EnqueueResetStreamFrame(stream_id,
+ request_priority,
+ RST_STREAM_PROTOCOL_ERROR,
+ "Pushed stream url was invalid: " + gurl.spec());
+ return false;
+ }
+
+ // Verify we have a valid stream association.
+ ActiveStreamMap::iterator associated_it =
+ active_streams_.find(associated_stream_id);
+ if (associated_it == active_streams_.end()) {
+ EnqueueResetStreamFrame(
+ stream_id,
+ request_priority,
+ RST_STREAM_INVALID_STREAM,
+ base::StringPrintf("Received push for inactive associated stream %d",
+ associated_stream_id));
+ return false;
+ }
+
+ // Check that the pushed stream advertises the same origin as its associated
+ // stream. Bypass this check if and only if this session is with a SPDY proxy
+ // that is trusted explicitly via the --trusted-spdy-proxy switch.
+ if (trusted_spdy_proxy_.Equals(host_port_pair())) {
+ // Disallow pushing of HTTPS content.
+ if (gurl.SchemeIs("https")) {
+ EnqueueResetStreamFrame(
+ stream_id,
+ request_priority,
+ RST_STREAM_REFUSED_STREAM,
+ base::StringPrintf("Rejected push of Cross Origin HTTPS content %d",
+ associated_stream_id));
+ }
+ } else {
+ GURL associated_url(associated_it->second.stream->GetUrlFromHeaders());
+ if (associated_url.GetOrigin() != gurl.GetOrigin()) {
+ EnqueueResetStreamFrame(
+ stream_id,
+ request_priority,
+ RST_STREAM_REFUSED_STREAM,
+ base::StringPrintf("Rejected Cross Origin Push Stream %d",
+ associated_stream_id));
+ return false;
+ }
+ }
+
+ // There should not be an existing pushed stream with the same path.
+ PushedStreamMap::iterator pushed_it =
+ unclaimed_pushed_streams_.lower_bound(gurl);
+ if (pushed_it != unclaimed_pushed_streams_.end() &&
+ pushed_it->first == gurl) {
+ EnqueueResetStreamFrame(
+ stream_id,
+ request_priority,
+ RST_STREAM_PROTOCOL_ERROR,
+ "Received duplicate pushed stream with url: " + gurl.spec());
+ return false;
+ }
+
+ scoped_ptr<SpdyStream> stream(new SpdyStream(SPDY_PUSH_STREAM,
+ GetWeakPtr(),
+ gurl,
+ request_priority,
+ stream_initial_send_window_size_,
+ stream_initial_recv_window_size_,
+ net_log_));
+ stream->set_stream_id(stream_id);
+
+ // In spdy4/http2 PUSH_PROMISE arrives on associated stream.
+ if (associated_it != active_streams_.end() && GetProtocolVersion() >= SPDY4) {
+ associated_it->second.stream->IncrementRawReceivedBytes(
+ last_compressed_frame_len_);
+ } else {
+ stream->IncrementRawReceivedBytes(last_compressed_frame_len_);
+ }
+
+ last_compressed_frame_len_ = 0;
+
+ DeleteExpiredPushedStreams();
+ PushedStreamMap::iterator inserted_pushed_it =
+ unclaimed_pushed_streams_.insert(
+ pushed_it,
+ std::make_pair(gurl, PushedStreamInfo(stream_id, time_func_())));
+ DCHECK(inserted_pushed_it != pushed_it);
+
+ InsertActivatedStream(stream.Pass());
+
+ ActiveStreamMap::iterator active_it = active_streams_.find(stream_id);
+ if (active_it == active_streams_.end()) {
+ NOTREACHED();
+ return false;
+ }
+
+ active_it->second.stream->OnPushPromiseHeadersReceived(headers);
+ DCHECK(active_it->second.stream->IsReservedRemote());
+ return true;
+}
+
void SpdySession::OnPushPromise(SpdyStreamId stream_id,
- SpdyStreamId promised_stream_id) {
- // TODO(akalin): Handle PUSH_PROMISE frames.
+ SpdyStreamId promised_stream_id,
+ const SpdyHeaderBlock& headers) {
+ CHECK(in_io_loop_);
+
+ if (net_log_.IsLogging()) {
+ net_log_.AddEvent(NetLog::TYPE_SPDY_SESSION_RECV_PUSH_PROMISE,
+ base::Bind(&NetLogSpdyPushPromiseReceivedCallback,
+ &headers,
+ stream_id,
+ promised_stream_id));
+ }
+
+ // Any priority will do.
+ // TODO(baranovich): pass parent stream id priority?
+ if (!TryCreatePushStream(promised_stream_id, stream_id, 0, headers))
+ return;
+
+ base::StatsCounter push_requests("spdy.pushed_streams");
+ push_requests.Increment();
}
void SpdySession::SendStreamWindowUpdate(SpdyStreamId stream_id,
@@ -2419,10 +2610,9 @@ void SpdySession::SendStreamWindowUpdate(SpdyStreamId stream_id,
void SpdySession::SendInitialData() {
DCHECK(enable_sending_initial_data_);
- DCHECK_NE(availability_state_, STATE_CLOSED);
if (send_connection_header_prefix_) {
- DCHECK_EQ(protocol_, kProtoHTTP2Draft04);
+ DCHECK_EQ(protocol_, kProtoSPDY4);
scoped_ptr<SpdyFrame> connection_header_prefix_frame(
new SpdyFrame(const_cast<char*>(kHttp2ConnectionHeaderPrefix),
kHttp2ConnectionHeaderPrefixSize,
@@ -2485,8 +2675,6 @@ void SpdySession::SendInitialData() {
void SpdySession::SendSettings(const SettingsMap& settings) {
- DCHECK_NE(availability_state_, STATE_CLOSED);
-
net_log_.AddEvent(
NetLog::TYPE_SPDY_SESSION_SEND_SETTINGS,
base::Bind(&NetLogSpdySendSettingsCallback, &settings));
@@ -2557,7 +2745,7 @@ void SpdySession::SendPrefacePingIfNoneInFlight() {
}
void SpdySession::SendPrefacePing() {
- WritePingFrame(next_ping_id_);
+ WritePingFrame(next_ping_id_, false);
}
void SpdySession::SendWindowUpdateFrame(SpdyStreamId stream_id,
@@ -2583,18 +2771,18 @@ void SpdySession::SendWindowUpdateFrame(SpdyStreamId stream_id,
EnqueueSessionWrite(priority, WINDOW_UPDATE, window_update_frame.Pass());
}
-void SpdySession::WritePingFrame(uint32 unique_id) {
+void SpdySession::WritePingFrame(uint32 unique_id, bool is_ack) {
DCHECK(buffered_spdy_framer_.get());
scoped_ptr<SpdyFrame> ping_frame(
- buffered_spdy_framer_->CreatePingFrame(unique_id));
+ buffered_spdy_framer_->CreatePingFrame(unique_id, is_ack));
EnqueueSessionWrite(HIGHEST, PING, ping_frame.Pass());
- if (net_log().IsLoggingAllEvents()) {
+ if (net_log().IsLogging()) {
net_log().AddEvent(
NetLog::TYPE_SPDY_SESSION_PING,
- base::Bind(&NetLogSpdyPingCallback, unique_id, "sent"));
+ base::Bind(&NetLogSpdyPingCallback, unique_id, is_ack, "sent"));
}
- if (unique_id % 2 != 0) {
+ if (!is_ack) {
next_ping_id_ += 2;
++pings_in_flight_;
PlanToCheckPingStatus();
@@ -2615,7 +2803,6 @@ void SpdySession::PlanToCheckPingStatus() {
void SpdySession::CheckPingStatus(base::TimeTicks last_check_time) {
CHECK(!in_io_loop_);
- DCHECK_NE(availability_state_, STATE_CLOSED);
// Check if we got a response back for all PINGs we had sent.
if (pings_in_flight_ == 0) {
@@ -2630,12 +2817,8 @@ void SpdySession::CheckPingStatus(base::TimeTicks last_check_time) {
if (delay.InMilliseconds() < 0 || last_activity_time_ < last_check_time) {
// Track all failed PING messages in a separate bucket.
- const base::TimeDelta kFailedPing =
- base::TimeDelta::FromInternalValue(INT_MAX);
- RecordPingRTTHistogram(kFailedPing);
- CloseSessionResult result =
- DoCloseSession(ERR_SPDY_PING_FAILED, "Failed ping.");
- DCHECK_EQ(result, SESSION_CLOSED_AND_REMOVED);
+ RecordPingRTTHistogram(base::TimeDelta::Max());
+ DoDrainSession(ERR_SPDY_PING_FAILED, "Failed ping.");
return;
}
@@ -2738,13 +2921,16 @@ void SpdySession::CompleteStreamRequest(
return;
base::WeakPtr<SpdyStream> stream;
- int rv = CreateStream(*pending_request, &stream);
+ int rv = TryCreateStream(pending_request, &stream);
if (rv == OK) {
DCHECK(stream);
pending_request->OnRequestCompleteSuccess(stream);
- } else {
- DCHECK(!stream);
+ return;
+ }
+ DCHECK(!stream);
+
+ if (rv != ERR_IO_PENDING) {
pending_request->OnRequestCompleteFailure(rv);
}
}
@@ -2765,9 +2951,6 @@ void SpdySession::OnWriteBufferConsumed(
// We can be called with |in_io_loop_| set if a write SpdyBuffer is
// deleted (e.g., a stream is closed due to incoming data).
- if (availability_state_ == STATE_CLOSED)
- return;
-
DCHECK_EQ(flow_control_state_, FLOW_CONTROL_STREAM_AND_SESSION);
if (consume_source == SpdyBuffer::DISCARD) {
@@ -2787,7 +2970,6 @@ void SpdySession::IncreaseSendWindowSize(int32 delta_window_size) {
// We can be called with |in_io_loop_| set if a SpdyBuffer is
// deleted (e.g., a stream is closed due to incoming data).
- DCHECK_NE(availability_state_, STATE_CLOSED);
DCHECK_EQ(flow_control_state_, FLOW_CONTROL_STREAM_AND_SESSION);
DCHECK_GE(delta_window_size, 1);
@@ -2795,13 +2977,12 @@ void SpdySession::IncreaseSendWindowSize(int32 delta_window_size) {
int32 max_delta_window_size = kint32max - session_send_window_size_;
if (delta_window_size > max_delta_window_size) {
RecordProtocolErrorHistogram(PROTOCOL_ERROR_INVALID_WINDOW_UPDATE_SIZE);
- CloseSessionResult result = DoCloseSession(
+ DoDrainSession(
ERR_SPDY_PROTOCOL_ERROR,
"Received WINDOW_UPDATE [delta: " +
- base::IntToString(delta_window_size) +
- "] for session overflows session_send_window_size_ [current: " +
- base::IntToString(session_send_window_size_) + "]");
- DCHECK_NE(result, SESSION_ALREADY_CLOSED);
+ base::IntToString(delta_window_size) +
+ "] for session overflows session_send_window_size_ [current: " +
+ base::IntToString(session_send_window_size_) + "]");
return;
}
@@ -2817,7 +2998,6 @@ void SpdySession::IncreaseSendWindowSize(int32 delta_window_size) {
}
void SpdySession::DecreaseSendWindowSize(int32 delta_window_size) {
- DCHECK_NE(availability_state_, STATE_CLOSED);
DCHECK_EQ(flow_control_state_, FLOW_CONTROL_STREAM_AND_SESSION);
// We only call this method when sending a frame. Therefore,
@@ -2843,9 +3023,6 @@ void SpdySession::OnReadBufferConsumed(
// We can be called with |in_io_loop_| set if a read SpdyBuffer is
// deleted (e.g., discarded by a SpdyReadQueue).
- if (availability_state_ == STATE_CLOSED)
- return;
-
DCHECK_EQ(flow_control_state_, FLOW_CONTROL_STREAM_AND_SESSION);
DCHECK_GE(consume_size, 1u);
DCHECK_LE(consume_size, static_cast<size_t>(kint32max));
@@ -2854,7 +3031,6 @@ void SpdySession::OnReadBufferConsumed(
}
void SpdySession::IncreaseRecvWindowSize(int32 delta_window_size) {
- DCHECK_NE(availability_state_, STATE_CLOSED);
DCHECK_EQ(flow_control_state_, FLOW_CONTROL_STREAM_AND_SESSION);
DCHECK_GE(session_unacked_recv_window_bytes_, 0);
DCHECK_GE(session_recv_window_size_, session_unacked_recv_window_bytes_);
@@ -2887,12 +3063,11 @@ void SpdySession::DecreaseRecvWindowSize(int32 delta_window_size) {
// negative. If we do, the receive window isn't being respected.
if (delta_window_size > session_recv_window_size_) {
RecordProtocolErrorHistogram(PROTOCOL_ERROR_RECEIVE_WINDOW_VIOLATION);
- CloseSessionResult result = DoCloseSession(
- ERR_SPDY_PROTOCOL_ERROR,
+ DoDrainSession(
+ ERR_SPDY_FLOW_CONTROL_ERROR,
"delta_window_size is " + base::IntToString(delta_window_size) +
" in DecreaseRecvWindowSize, which is larger than the receive " +
"window size of " + base::IntToString(session_recv_window_size_));
- DCHECK_EQ(result, SESSION_CLOSED_BUT_NOT_REMOVED);
return;
}
@@ -2919,10 +3094,11 @@ void SpdySession::ResumeSendStalledStreams() {
// have to worry about streams being closed, as well as ourselves
// being closed.
- while (availability_state_ != STATE_CLOSED && !IsSendStalled()) {
+ while (!IsSendStalled()) {
size_t old_size = 0;
- if (DCHECK_IS_ON())
- old_size = GetTotalSize(stream_send_unstall_queue_);
+#if DCHECK_IS_ON
+ old_size = GetTotalSize(stream_send_unstall_queue_);
+#endif
SpdyStreamId stream_id = PopStreamToPossiblyResume();
if (stream_id == 0)