summaryrefslogtreecommitdiffstats
path: root/net/spdy/spdy_session.cc
diff options
context:
space:
mode:
Diffstat (limited to 'net/spdy/spdy_session.cc')
-rw-r--r--net/spdy/spdy_session.cc185
1 files changed, 184 insertions, 1 deletions
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index e88d587..08b9d92 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -172,6 +172,23 @@ class NetLogSpdyRstParameter : public NetLog::EventParameters {
DISALLOW_COPY_AND_ASSIGN(NetLogSpdyRstParameter);
};
+class NetLogSpdyPingParameter : public NetLog::EventParameters {
+ public:
+ explicit NetLogSpdyPingParameter(uint32 unique_id) : unique_id_(unique_id) {}
+
+ virtual Value* ToValue() const {
+ DictionaryValue* dict = new DictionaryValue();
+ dict->SetInteger("unique_id", unique_id_);
+ return dict;
+ }
+
+ private:
+ ~NetLogSpdyPingParameter() {}
+ const uint32 unique_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetLogSpdyPingParameter);
+};
+
class NetLogSpdyGoAwayParameter : public NetLog::EventParameters {
public:
NetLogSpdyGoAwayParameter(spdy::SpdyStreamId last_stream_id,
@@ -210,6 +227,18 @@ bool SpdySession::use_flow_control_ = false;
// static
size_t SpdySession::max_concurrent_stream_limit_ = 256;
+// static
+bool SpdySession::enable_ping_based_connection_checking_ = true;
+
+// static
+int SpdySession::connection_at_risk_of_loss_ms_ = 0;
+
+// static
+int SpdySession::trailing_ping_delay_time_ms_ = 1000;
+
+// static
+int SpdySession::hung_interval_ms_ = 10000;
+
SpdySession::SpdySession(const HostPortProxyPair& host_port_proxy_pair,
SpdySessionPool* spdy_session_pool,
SpdySettingsStorage* spdy_settings,
@@ -242,6 +271,12 @@ SpdySession::SpdySession(const HostPortProxyPair& host_port_proxy_pair,
sent_settings_(false),
received_settings_(false),
stalled_streams_(0),
+ pings_in_flight_(0),
+ next_ping_id_(1),
+ received_data_time_(base::TimeTicks::Now()),
+ trailing_ping_pending_(false),
+ check_ping_status_pending_(false),
+ need_to_send_ping_(false),
initial_send_window_size_(spdy::kSpdyStreamInitialWindowSize),
initial_recv_window_size_(spdy::kSpdyStreamInitialWindowSize),
net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SPDY_SESSION)) {
@@ -466,6 +501,8 @@ int SpdySession::WriteSynStream(
const scoped_refptr<SpdyStream>& stream = active_streams_[stream_id];
CHECK_EQ(stream->stream_id(), stream_id);
+ SendPrefacePingIfNoneInFlight();
+
scoped_ptr<spdy::SpdySynStreamControlFrame> syn_frame(
spdy_framer_.CreateSynStream(
stream_id, 0,
@@ -484,6 +521,12 @@ int SpdySession::WriteSynStream(
new NetLogSpdySynParameter(headers, flags, stream_id, 0)));
}
+ // Some servers don't like too many pings, so we limit our current sending to
+ // no more than one ping for any syn sent. To do this, we avoid ever setting
+ // this to true unless we send a syn (which we have just done). This approach
+ // may change over time as servers change their responses to pings.
+ need_to_send_ping_ = true;
+
return ERR_IO_PENDING;
}
@@ -497,6 +540,8 @@ int SpdySession::WriteStreamData(spdy::SpdyStreamId stream_id,
if (!stream)
return ERR_INVALID_SPDY_STREAM;
+ SendPrefacePingIfNoneInFlight();
+
if (len > kMaxSpdyFrameChunkSize) {
len = kMaxSpdyFrameChunkSize;
flags = static_cast<spdy::SpdyDataFlags>(flags & ~spdy::DATA_FLAG_FIN);
@@ -563,7 +608,6 @@ void SpdySession::ResetStream(
priority = stream->priority();
}
QueueFrame(rst_frame.get(), priority, NULL);
-
DeleteStream(stream_id, ERR_SPDY_PROTOCOL_ERROR);
}
@@ -604,6 +648,8 @@ void SpdySession::OnReadComplete(int bytes_read) {
bytes_received_ += bytes_read;
+ received_data_time_ = base::TimeTicks::Now();
+
// The SpdyFramer will use callbacks onto |this| as it parses frames.
// When errors occur, those callbacks can lead to teardown of all references
// to |this|, so maintain a reference to self during this call for safe
@@ -1221,6 +1267,9 @@ void SpdySession::OnControl(const spdy::SpdyControlFrame* frame) {
case spdy::GOAWAY:
OnGoAway(*reinterpret_cast<const spdy::SpdyGoAwayControlFrame*>(frame));
break;
+ case spdy::PING:
+ OnPing(*reinterpret_cast<const spdy::SpdyPingControlFrame*>(frame));
+ break;
case spdy::SETTINGS:
OnSettings(
*reinterpret_cast<const spdy::SpdySettingsControlFrame*>(frame));
@@ -1250,6 +1299,17 @@ void SpdySession::OnControl(const spdy::SpdyControlFrame* frame) {
}
}
+bool SpdySession::OnControlFrameHeaderData(spdy::SpdyStreamId stream_id,
+ const char* header_data,
+ size_t len) {
+ DCHECK(false);
+ return false;
+}
+
+void SpdySession::OnDataFrameHeader(const spdy::SpdyDataFrame* frame) {
+ DCHECK(false);
+}
+
void SpdySession::OnRst(const spdy::SpdyRstStreamControlFrame& frame) {
spdy::SpdyStreamId stream_id = frame.stream_id();
@@ -1296,6 +1356,32 @@ void SpdySession::OnGoAway(const spdy::SpdyGoAwayControlFrame& frame) {
// closed.
}
+void SpdySession::OnPing(const spdy::SpdyPingControlFrame& frame) {
+ net_log_.AddEvent(
+ NetLog::TYPE_SPDY_SESSION_PING,
+ make_scoped_refptr(new NetLogSpdyPingParameter(frame.unique_id())));
+
+ // Send response to a PING from server.
+ if (frame.unique_id() % 2 == 0) {
+ WritePingFrame(frame.unique_id());
+ return;
+ }
+
+ --pings_in_flight_;
+ if (pings_in_flight_ < 0) {
+ CloseSessionOnError(net::ERR_SPDY_PROTOCOL_ERROR, true);
+ return;
+ }
+
+ if (pings_in_flight_ > 0)
+ return;
+
+ if (!need_to_send_ping_)
+ return;
+
+ PlanToSendTrailingPing();
+}
+
void SpdySession::OnSettings(const spdy::SpdySettingsControlFrame& frame) {
spdy::SpdySettings settings;
if (spdy_framer_.ParseSettings(&frame, &settings)) {
@@ -1434,6 +1520,103 @@ void SpdySession::HandleSettings(const spdy::SpdySettings& settings) {
}
}
+void SpdySession::SendPrefacePingIfNoneInFlight() {
+ if (pings_in_flight_ || trailing_ping_pending_ ||
+ !enable_ping_based_connection_checking_)
+ return;
+
+ const base::TimeDelta kConnectionAtRiskOfLoss =
+ base::TimeDelta::FromMilliseconds(connection_at_risk_of_loss_ms_);
+
+ base::TimeTicks now = base::TimeTicks::Now();
+ // If we haven't heard from server, then send a preface-PING.
+ if ((now - received_data_time_) > kConnectionAtRiskOfLoss)
+ SendPrefacePing();
+
+ PlanToSendTrailingPing();
+}
+
+void SpdySession::SendPrefacePing() {
+ // TODO(rtenneti): Send preface pings when more servers support additional
+ // pings.
+ // WritePingFrame(next_ping_id_);
+}
+
+void SpdySession::PlanToSendTrailingPing() {
+ if (trailing_ping_pending_)
+ return;
+
+ trailing_ping_pending_ = true;
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ method_factory_.NewRunnableMethod(&SpdySession::SendTrailingPing),
+ trailing_ping_delay_time_ms_);
+}
+
+void SpdySession::SendTrailingPing() {
+ DCHECK(trailing_ping_pending_);
+ trailing_ping_pending_ = false;
+ WritePingFrame(next_ping_id_);
+}
+
+void SpdySession::WritePingFrame(uint32 unique_id) {
+ scoped_ptr<spdy::SpdyPingControlFrame> ping_frame(
+ spdy_framer_.CreatePingFrame(next_ping_id_));
+ QueueFrame(ping_frame.get(), SPDY_PRIORITY_HIGHEST, NULL);
+
+ if (net_log().IsLoggingAllEvents()) {
+ net_log().AddEvent(
+ NetLog::TYPE_SPDY_SESSION_PING,
+ make_scoped_refptr(new NetLogSpdyPingParameter(next_ping_id_)));
+ }
+ if (unique_id % 2 != 0) {
+ next_ping_id_ += 2;
+ ++pings_in_flight_;
+ need_to_send_ping_ = false;
+ PlanToCheckPingStatus();
+ }
+}
+
+void SpdySession::PlanToCheckPingStatus() {
+ if (check_ping_status_pending_)
+ return;
+
+ check_ping_status_pending_ = true;
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ method_factory_.NewRunnableMethod(
+ &SpdySession::CheckPingStatus, base::TimeTicks::Now()),
+ hung_interval_ms_);
+}
+
+void SpdySession::CheckPingStatus(base::TimeTicks last_check_time) {
+ // Check if we got a response back for all PINGs we had sent.
+ if (pings_in_flight_ == 0) {
+ check_ping_status_pending_ = false;
+ return;
+ }
+
+ DCHECK(check_ping_status_pending_);
+
+ const base::TimeDelta kHungInterval =
+ base::TimeDelta::FromMilliseconds(hung_interval_ms_);
+
+ base::TimeTicks now = base::TimeTicks::Now();
+ base::TimeDelta delay = kHungInterval - (now - received_data_time_);
+
+ if (delay.InMilliseconds() < 0 || received_data_time_ < last_check_time) {
+ DCHECK(now - received_data_time_ > kHungInterval);
+ CloseSessionOnError(net::ERR_SPDY_PING_FAILED, true);
+ return;
+ }
+
+ // Check the status of connection after a delay.
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ method_factory_.NewRunnableMethod(&SpdySession::CheckPingStatus, now),
+ delay.InMilliseconds());
+}
+
void SpdySession::RecordHistograms() {
UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsPerSession",
streams_initiated_count_,