Chromium Code Reviews| Index: net/spdy/spdy_session.cc |
| =================================================================== |
| --- net/spdy/spdy_session.cc (revision 105385) |
| +++ net/spdy/spdy_session.cc (working copy) |
| @@ -172,6 +172,23 @@ |
| 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, |
| @@ -213,6 +230,18 @@ |
| // 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_ = 10000; |
| + |
| +// 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, |
| @@ -246,6 +275,12 @@ |
| 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), |
| + last_sent_was_ping_(false), |
| initial_send_window_size_(spdy::kSpdyStreamInitialWindowSize), |
| initial_recv_window_size_(spdy::kSpdyStreamInitialWindowSize), |
| net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SPDY_SESSION)), |
| @@ -474,6 +509,8 @@ |
| 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, |
| @@ -491,6 +528,7 @@ |
| make_scoped_refptr( |
| new NetLogSpdySynParameter(headers, flags, stream_id, 0))); |
| } |
| + last_sent_was_ping_ = false; |
| return ERR_IO_PENDING; |
| } |
| @@ -505,6 +543,8 @@ |
| if (!stream) |
| return ERR_INVALID_SPDY_STREAM; |
| + SendPrefacePingIfNoneInFlight(); |
| + |
| if (len > kMaxSpdyFrameChunkSize) { |
| len = kMaxSpdyFrameChunkSize; |
| flags = static_cast<spdy::SpdyDataFlags>(flags & ~spdy::DATA_FLAG_FIN); |
| @@ -544,6 +584,7 @@ |
| scoped_ptr<spdy::SpdyDataFrame> frame( |
| spdy_framer_.CreateDataFrame(stream_id, data->data(), len, flags)); |
| QueueFrame(frame.get(), stream->priority(), stream); |
| + last_sent_was_ping_ = false; |
| return ERR_IO_PENDING; |
| } |
| @@ -571,7 +612,7 @@ |
| priority = stream->priority(); |
| } |
| QueueFrame(rst_frame.get(), priority, NULL); |
| - |
| + last_sent_was_ping_ = false; |
|
willchan no longer on Chromium
2011/10/15 08:08:03
I feel like it'd be safer if you set this in Queue
jar (doing other things)
2011/10/15 09:00:52
The problem(?) is that we send pings via QueueFram
willchan no longer on Chromium
2011/10/15 20:17:40
Yes, this is one of those typical tradeoffs. Do yo
jar (doing other things)
2011/10/15 23:25:11
I'm fine with the factoring you suggested if it ma
willchan no longer on Chromium
2011/10/15 23:52:28
OK
On 2011/10/15 23:25:11, jar wrote:
|
| DeleteStream(stream_id, ERR_SPDY_PROTOCOL_ERROR); |
| } |
| @@ -612,6 +653,8 @@ |
| 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 |
| @@ -1229,6 +1272,9 @@ |
| 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)); |
| @@ -1317,6 +1363,32 @@ |
| // 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 reponse to a PING from Server. |
|
willchan no longer on Chromium
2011/10/15 08:08:03
s/reponse/response/, s/Server/server/g
ramant (doing other things)
2011/10/15 23:33:11
Done.
|
| + 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 (last_sent_was_ping_) |
| + return; |
| + |
| + PlanToSendTrailingPing(); |
| +} |
| + |
| void SpdySession::OnSettings(const spdy::SpdySettingsControlFrame& frame) { |
| spdy::SpdySettings settings; |
| if (spdy_framer_.ParseSettings(&frame, &settings)) { |
| @@ -1374,6 +1446,7 @@ |
| scoped_ptr<spdy::SpdyWindowUpdateControlFrame> window_update_frame( |
| spdy_framer_.CreateWindowUpdate(stream_id, delta_window_size)); |
| QueueFrame(window_update_frame.get(), stream->priority(), stream); |
| + last_sent_was_ping_ = false; |
| } |
| // Given a cwnd that we would have sent to the server, modify it based on the |
| @@ -1438,6 +1511,7 @@ |
| spdy_framer_.CreateSettings(settings)); |
| sent_settings_ = true; |
| QueueFrame(settings_frame.get(), 0, NULL); |
| + last_sent_was_ping_ = false; |
| } |
| void SpdySession::HandleSettings(const spdy::SpdySettings& settings) { |
| @@ -1455,6 +1529,101 @@ |
| } |
| } |
| +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() { |
| + 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_; |
| + last_sent_was_ping_ = true; |
| + 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_, |