Index: net/spdy/spdy_session.h |
=================================================================== |
--- net/spdy/spdy_session.h (revision 105385) |
+++ net/spdy/spdy_session.h (working copy) |
@@ -104,7 +104,8 @@ |
// NOTE: This function can have false negatives on some platforms. |
bool VerifyDomainAuthentication(const std::string& domain); |
- // Send the SYN frame for |stream_id|. |
+ // Send the SYN frame for |stream_id|. This also sends PING message to check |
+ // the status of the connection. |
int WriteSynStream( |
spdy::SpdyStreamId stream_id, |
RequestPriority priority, |
@@ -156,6 +157,14 @@ |
return max_concurrent_stream_limit_; |
} |
+ // Enable sending of PING frame with each request. |
+ static void set_enable_ping_based_connection_checking(bool enable) { |
+ enable_ping_based_connection_checking_ = enable; |
+ } |
+ static bool enable_ping_based_connection_checking() { |
+ return enable_ping_based_connection_checking_; |
+ } |
+ |
// The initial max concurrent streams per session, can be overridden by the |
// server via SETTINGS. |
static void set_init_max_concurrent_streams(size_t value) { |
@@ -217,6 +226,8 @@ |
private: |
friend class base::RefCounted<SpdySession>; |
+ // Allow tests to access our innards for testing purposes. |
+ FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, Ping); |
FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, GetActivePushStream); |
struct PendingCreateStream { |
@@ -277,6 +288,7 @@ |
const linked_ptr<spdy::SpdyHeaderBlock>& headers); |
void OnRst(const spdy::SpdyRstStreamControlFrame& frame); |
void OnGoAway(const spdy::SpdyGoAwayControlFrame& frame); |
+ void OnPing(const spdy::SpdyPingControlFrame& frame); |
void OnSettings(const spdy::SpdySettingsControlFrame& frame); |
void OnWindowUpdate(const spdy::SpdyWindowUpdateControlFrame& frame); |
@@ -291,6 +303,31 @@ |
// SETTINGS ontrol frame, update our SpdySession accordingly. |
void HandleSettings(const spdy::SpdySettings& settings); |
+ // Send the PING (preface-PING and trailing-PING) frames. |
+ void SendPrefacePingIfNoneInFlight(); |
+ |
+ // Send PING if there are no PINGs in flight and we haven't heard from server. |
+ void SendPrefacePing(); |
+ |
+ // Send a PING after delay. Don't post a PING if there is already |
+ // a trailing PING pending. |
+ void PlanToSendTrailingPing(); |
+ |
+ // Send a PING if there is no |trailing_ping_pending_|. This PING verifies |
+ // that the requests are being received by the server. |
+ void SendTrailingPing(); |
+ |
+ // Send the PING frame. |
+ void WritePingFrame(uint32 unique_id); |
+ |
+ // Post a CheckPingStatus call after delay. Don't post if there is already |
+ // CheckPingStatus running. |
+ void PlanToCheckPingStatus(); |
+ |
+ // Check the status of the connection. It calls |CloseSessionOnError| if we |
+ // haven't received any data in |kHungInterval| time period. |
+ void CheckPingStatus(base::TimeTicks last_check_time); |
+ |
// Start reading from the socket. |
// Returns OK on success, or an error on failure. |
net::Error ReadSocket(); |
@@ -348,6 +385,40 @@ |
virtual void OnDataFrameHeader(const spdy::SpdyDataFrame* frame); |
+ // -------------------------- |
+ // Helper methods for testing |
+ // -------------------------- |
+ static void set_connection_at_risk_of_loss_ms(int duration) { |
+ connection_at_risk_of_loss_ms_ = duration; |
+ } |
+ static int connection_at_risk_of_loss_ms() { |
+ return connection_at_risk_of_loss_ms_; |
+ } |
+ |
+ static void set_trailing_ping_delay_time_ms(int duration) { |
+ trailing_ping_delay_time_ms_ = duration; |
+ } |
+ static int trailing_ping_delay_time_ms() { |
+ return trailing_ping_delay_time_ms_; |
+ } |
+ |
+ static void set_hung_interval_ms(int duration) { |
+ hung_interval_ms_ = duration; |
+ } |
+ static int hung_interval_ms() { |
+ return hung_interval_ms_; |
+ } |
+ |
+ int64 pings_in_flight() const { return pings_in_flight_; } |
+ |
+ uint32 next_ping_id() const { return next_ping_id_; } |
+ |
+ base::TimeTicks received_data_time() const { return received_data_time_; } |
+ |
+ bool trailing_ping_pending() const { return trailing_ping_pending_; } |
+ |
+ bool check_ping_status_pending() const { return check_ping_status_pending_; } |
+ |
// Callbacks for the Spdy session. |
OldCompletionCallbackImpl<SpdySession> read_callback_; |
OldCompletionCallbackImpl<SpdySession> write_callback_; |
@@ -437,6 +508,28 @@ |
// frame. |
int stalled_streams_; // Count of streams that were ever stalled. |
+ // Count of all pings on the wire, for which we have not gotten a response. |
+ int64 pings_in_flight_; |
+ |
+ // This is the next ping_id (unique_id) to be sent in PING frame. |
+ uint32 next_ping_id_; |
+ |
+ // This is the last time we have received data. |
+ base::TimeTicks received_data_time_; |
+ |
+ // Indicate if we have already scheduled a delayed task to send a trailing |
+ // ping (and we never have more than one scheduled at a time). |
+ bool trailing_ping_pending_; |
+ |
+ // Indicate if we have already scheduled a delayed task to check the ping |
+ // status. |
+ bool check_ping_status_pending_; |
+ |
+ // Indicate if the last data we sent was a ping (generally, a trailing ping). |
+ // This helps us to decide if we need yet another trailing ping, or if it |
+ // would be a waste of effort (and MUST not be done). |
+ bool last_sent_was_ping_; |
+ |
// Initial send window size for the session; can be changed by an |
// arriving SETTINGS frame; newly created streams use this value for the |
// initial send window size. |
@@ -457,6 +550,36 @@ |
static bool use_flow_control_; |
static size_t init_max_concurrent_streams_; |
static size_t max_concurrent_stream_limit_; |
+ |
+ // This enables or disables connection health checking system. |
+ static bool enable_ping_based_connection_checking_; |
+ |
+ // |connection_at_risk_of_loss_ms_| is an optimization to avoid sending |
+ // wasteful preface pings (when we just got some data). |
+ // |
+ // If it is zero (the most conservative figure), then we always send the |
+ // preface ping (when none are in flight). |
+ // |
+ // It is common for TCP/IP sessions to time out in about 3-5 minutes. |
+ // Certainly if it has been more than 3 minutes, we do want to send a preface |
+ // ping. |
+ // |
+ // We don't think any connection will time out in under about 10 seconds. So |
+ // this might as well be set to something conservative like 10 seconds. Later, |
+ // we could adjust it to send fewer pings perhaps. |
+ static int connection_at_risk_of_loss_ms_; |
+ |
+ // This is the amount of time (in milliseconds) we wait before sending a |
+ // trailing ping. We use a trailing ping (sent after all data) to get an |
+ // effective acknowlegement from the server that it has indeed received all |
+ // (prior) data frames. With that assurance, we are willing to enter into a |
+ // wait state for responses to our last data frame(s) without further pings. |
+ static int trailing_ping_delay_time_ms_; |
+ |
+ // The amount of time (in milliseconds) that we are willing to tolerate with |
+ // no data received (of any form), while there is a ping in flight, before we |
+ // declare the connection to be hung. |
+ static int hung_interval_ms_; |
}; |
class NetLogSpdySynParameter : public NetLog::EventParameters { |