shill: Make multiple TCP connections in ConnectionHealthChecker and
decide based on the whole sample

BUG=chromium:232136
TEST=(1) Build and run unit-tests
     (2) Test that there aren't many false positives in out-of-credit
         detection.

Change-Id: I3fa9d926536a2085dcdd8e6f7f92363e437a0b9f
Reviewed-on: https://gerrit.chromium.org/gerrit/48860
Commit-Queue: Prathmesh Prabhu <pprabhu@chromium.org>
Reviewed-by: Prathmesh Prabhu <pprabhu@chromium.org>
Tested-by: Prathmesh Prabhu <pprabhu@chromium.org>
diff --git a/connection_health_checker.h b/connection_health_checker.h
index 20fca7a..6bc870e 100644
--- a/connection_health_checker.h
+++ b/connection_health_checker.h
@@ -9,6 +9,7 @@
 
 #include <base/basictypes.h>
 #include <base/callback.h>
+#include <base/cancelable_callback.h>
 #include <base/memory/scoped_ptr.h>
 #include <base/memory/scoped_vector.h>
 #include <base/memory/weak_ptr.h>
@@ -40,16 +41,19 @@
  public:
   typedef std::vector<IPAddress> IPAddresses;
 
-  // TODO(pprabhu): Rename kResultElongatedTimeWait to kResultTearDownFailure.
   enum Result {
     // There was some problem in the setup of ConnctionHealthChecker.
     // Could not attempt a tcp connection.
     kResultUnknown,
+    // TODO(pprabhu) Deprecated. Remove.
+    // crbug.com/234734
     // New health check request made successfully. The result of the health
     // check is returned asynchronously.
     kResultInProgress,
     // Failed to create TCP connection. Condition -(1)-.
     kResultConnectionFailure,
+    // TODO(pprabhu) Deprecated. Remove.
+    // crbug.com/234734
     // Failed to destroy TCP connection. Condition -(2)-.
     kResultElongatedTimeWait,
     // Failed to send data on TCP connection. Condition -(2)-.
@@ -90,63 +94,136 @@
 
   // Accessors.
   const IPAddresses &remote_ips() const { return remote_ips_; }
-  void set_run_data_test(bool val) { run_data_test_ = val; }
-  virtual bool health_check_in_progress() const {
-      return health_check_in_progress_;
-  }
+  virtual bool health_check_in_progress() const;
 
+
+ protected:
+  // For unit-tests.
+  void set_dispatcher(EventDispatcher *dispatcher) {
+    dispatcher_ = dispatcher;
+  }
+  void set_sock_fd(int sock_fd) { sock_fd_ = sock_fd; }
+  short num_connection_failures() const { return num_connection_failures_; }
+  void set_num_connection_failures(short val) {
+    num_connection_failures_ = val;
+  }
+  short num_tx_queue_polling_attempts() const {
+    return num_tx_queue_polling_attempts_;
+  }
+  void set_num_tx_queue_polling_attempts(short val) {
+    num_tx_queue_polling_attempts_ = val;
+  }
+  short num_congested_queue_detected() const {
+    return num_congested_queue_detected_;
+  }
+  void set_num_congested_queue_detected(short val) {
+    num_congested_queue_detected_ = val;
+  }
+  short num_successful_sends() const { return num_successful_sends_; }
+  void set_num_successful_sends(short val) {
+    num_successful_sends_ = val;
+  }
+  void set_old_transmit_queue_value(uint64 val) {
+    old_transmit_queue_value_ = val;
+  }
+  Result health_check_result() const { return health_check_result_; }
  private:
   friend class ConnectionHealthCheckerTest;
   FRIEND_TEST(ConnectionHealthCheckerTest, GarbageCollectDNSClients);
   FRIEND_TEST(ConnectionHealthCheckerTest, GetSocketInfo);
-  FRIEND_TEST(ConnectionHealthCheckerTest, SendData);
-  FRIEND_TEST(ConnectionHealthCheckerTest, ShutDown);
+  FRIEND_TEST(ConnectionHealthCheckerTest, NextHealthCheckSample);
+  FRIEND_TEST(ConnectionHealthCheckerTest, OnConnectionComplete);
+  FRIEND_TEST(ConnectionHealthCheckerTest, VerifySentData);
 
   // Time to wait for DNS server.
   static const int kDNSTimeoutMilliseconds;
-  // Number of connection attempts before failure per health check request.
-  static const int kMaxConnectionAttempts;
+  static const int kInvalidSocket;
+  // After |kMaxFailedConnectionAttempts| failed attempts to connect, give up
+  // health check and return failure.
+  static const int kMaxFailedConnectionAttempts;
+  // After sending a small amount of data, attempt |kMaxSentDataPollingAttempts|
+  // times to see if the data was sent successfully.
+  static const int kMaxSentDataPollingAttempts;
+  // After |kMinCongestedQueueAttempts| to send data indicate a congested tx
+  // queue, finish health check and report a congested queue.
+  static const int kMinCongestedQueueAttempts;
+  // After sending data |kMinSuccessfulAttempts| times succesfully, finish
+  // health check and report a healthy connection.
+  static const int kMinSuccessfulSendAttempts;
   // Number of DNS queries to be spawned when a new remote URL is added.
   static const int kNumDNSQueries;
+  // Time to wait before testing successful data transfer / disconnect after
+  // request is made on the device.
+  static const int kTCPStateUpdateWaitMilliseconds;
+
   static const uint16 kRemotePort;
 
+  // Callback for DnsClient
+  void GetDNSResult(const Error &error, const IPAddress &ip);
+  void GarbageCollectDNSClients();
+
   // Start a new AsyncConnection with callback set to OnConnectionComplete().
-  void SetupTcpConnection();
+  void NextHealthCheckSample();
+  void ReportResult();
 
   // Callback for AsyncConnection.
   // Observe the setup connection to test health state
   void OnConnectionComplete(bool success, int sock_fd);
 
-  // Callback for DnsClient
-  void GetDNSResult(const Error &error, const IPAddress &ip);
-
-  void TryNextIP();
-  Result SendData(int sock_fd);
-  Result ShutDown(int sock_fd);
+  void VerifySentData();
   bool GetSocketInfo(int sock_fd, SocketInfo *sock_info);
-  void GarbageCollectDNSClients();
 
+  void SetSocketDescriptor(int sock_fd);
+  void ClearSocketDescriptor();
+
+  // The connection on which the health check is being run.
   ConnectionRefPtr connection_;
   EventDispatcher *dispatcher_;
+
+  // The client function to call to report the result.
   base::Callback<void(Result)> result_callback_;
 
-  IPAddresses remote_ips_;
-  scoped_ptr<SocketInfoReader> socket_info_reader_;
   scoped_ptr<Sockets> socket_;
   base::WeakPtrFactory<ConnectionHealthChecker> weak_ptr_factory_;
+
+  // Set of IPs to create TCP connection with for the health check.
+  IPAddresses remote_ips_;
+  // Callback passed to |tcp_connection_| to report an established TCP
+  // connection.
   const base::Callback<void(bool, int)> connection_complete_callback_;
-  const base::Callback<void(const Error&, const IPAddress&)>
-      dns_client_callback_;
+  // Active TCP connection during health check.
   scoped_ptr<AsyncConnection> tcp_connection_;
+  const base::Callback<void(void)> report_result_;
+  // Active socket for |tcp_connection_| during an active health check.
+  int sock_fd_;
+  // Interface to read TCP connection information from the system.
+  scoped_ptr<SocketInfoReader> socket_info_reader_;
+
   DNSClientFactory *dns_client_factory_;
   ScopedVector<DNSClient> dns_clients_;
-  // If true, HealthChecker attempts to send a small amount of data over
-  // the network during the test. Otherwise, the inference is based on
-  // the connection open/close behaviour.
-  // Default: true
-  bool run_data_test_;
+  const base::Callback<void(const Error&, const IPAddress&)>
+      dns_client_callback_;
+
+  // Store the old value of the transmit queue to verify that data sent on the
+  // connection is actually transmitted.
+  uint64 old_transmit_queue_value_;
+  // Callback to post a delayed check on whether data sent on the TCP connection
+  // was successfully transmitted.
+  base::CancelableClosure verify_sent_data_callback_;
+
   bool health_check_in_progress_;
-  short num_connection_attempts_;
+  // Number of connection failures in currently active health check.
+  short num_connection_failures_;
+  // Number of times we have checked the tx-queue for the current send attempt.
+  short num_tx_queue_polling_attempts_;
+  // Number of out of credit scenarios detected in currentl health check.
+  short num_congested_queue_detected_;
+  // Number of successful send attempts currently active health check.
+  short num_successful_sends_;
+
+  // Temporarily store the result of health check so that |report_result_|
+  // can report it.
+  Result health_check_result_;
 
   DISALLOW_COPY_AND_ASSIGN(ConnectionHealthChecker);
 };