C++程序  |  248行  |  9.25 KB

//
// Copyright (C) 2013 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#ifndef SHILL_CONNECTION_HEALTH_CHECKER_H_
#define SHILL_CONNECTION_HEALTH_CHECKER_H_

#include <memory>
#include <string>
#include <vector>

#include <base/callback.h>
#include <base/cancelable_callback.h>
#include <base/macros.h>
#include <base/memory/scoped_vector.h>
#include <base/memory/weak_ptr.h>
#include <gtest/gtest_prod.h>

#include "shill/net/sockets.h"
#include "shill/refptr_types.h"
#include "shill/socket_info.h"

namespace shill {

class AsyncConnection;
class DNSClient;
class DNSClientFactory;
class Error;
class EventDispatcher;
class IPAddress;
class IPAddressStore;
class SocketInfoReader;

// The ConnectionHealthChecker class implements the facilities to test
// connectivity status on some connection asynchronously.
// In particular, the class can distinguish between three states of the
// connection:
//   -(1)- No connectivity (TCP connection can not be established)
//   -(2)- Partial connectivity (TCP connection can be established, but no data
//         transfer)
//   -(3)- Connectivity OK (TCP connection established, is healthy)
class ConnectionHealthChecker {
 public:
  enum Result {
    // There was some problem in the setup of ConnctionHealthChecker.
    // Could not attempt a tcp connection.
    kResultUnknown,
    // Failed to create TCP connection. Condition -(1)-.
    kResultConnectionFailure,
    // Failed to send data on TCP connection. Condition -(2)-.
    kResultCongestedTxQueue,
    // Condition -(3)-.
    kResultSuccess
  };

  ConnectionHealthChecker(ConnectionRefPtr connection,
                          EventDispatcher* dispatcher,
                          IPAddressStore* remote_ips,
                          const base::Callback<void(Result)>& result_callback);
  virtual ~ConnectionHealthChecker();

  // A new ConnectionHealthChecker is created with a default URL to attempt the
  // TCP connection with. Add a URL to try.
  virtual void AddRemoteURL(const std::string& url_string);

  // Name resolution can fail in conditions -(1)- and -(2)-. Add an IP address
  // to attempt the TCP connection with.
  virtual void AddRemoteIP(IPAddress ip);

  // Change the associated Connection on the Device.
  // This will restart any ongoing health check. Any ongoing DNS query will be
  // dropped (not restarted).
  virtual void SetConnection(ConnectionRefPtr connection);

  // Start a connection health check. The health check involves one or more
  // attempts at establishing and using a TCP connection. |result_callback_| is
  // called with the final result of the check. |result_callback_| will always
  // be called after a call to Start() unless Stop() is called in the meantime.
  // |result_callback_| may be called before Start() completes.
  //
  // Calling Start() while a health check is in progress is a no-op.
  virtual void Start();

  // Stop the current health check. No callback is called as a side effect of
  // this function.
  //
  // Calling Stop() on a Stop()ed health check is a no-op.
  virtual void Stop();

  static const char* ResultToString(Result result);

  // Accessors.
  const IPAddressStore* remote_ips() const { return remote_ips_; }
  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; }
  int16_t num_connection_failures() const { return num_connection_failures_; }
  void set_num_connection_failures(int16_t val) {
    num_connection_failures_ = val;
  }
  int16_t num_tx_queue_polling_attempts() const {
    return num_tx_queue_polling_attempts_;
  }
  void set_num_tx_queue_polling_attempts(int16_t val) {
    num_tx_queue_polling_attempts_ = val;
  }
  int16_t num_congested_queue_detected() const {
    return num_congested_queue_detected_;
  }
  void set_num_congested_queue_detected(int16_t val) {
    num_congested_queue_detected_ = val;
  }
  int16_t num_successful_sends() const { return num_successful_sends_; }
  void set_num_successful_sends(int16_t val) {
    num_successful_sends_ = val;
  }
  void set_old_transmit_queue_value(uint64_t val) {
    old_transmit_queue_value_ = val;
  }
  Result health_check_result() const { return health_check_result_; }
  AsyncConnection* tcp_connection() const { return tcp_connection_.get(); }
  Connection* connection() const { return connection_.get(); }

 private:
  friend class ConnectionHealthCheckerTest;
  FRIEND_TEST(ConnectionHealthCheckerTest, GarbageCollectDNSClients);
  FRIEND_TEST(ConnectionHealthCheckerTest, GetSocketInfo);
  FRIEND_TEST(ConnectionHealthCheckerTest, NextHealthCheckSample);
  FRIEND_TEST(ConnectionHealthCheckerTest, OnConnectionComplete);
  FRIEND_TEST(ConnectionHealthCheckerTest, SetConnection);
  FRIEND_TEST(ConnectionHealthCheckerTest, VerifySentData);

  // List of static IPs for connection health check.
  static const char* kDefaultRemoteIPPool[];
  // Time to wait for DNS server.
  static const int kDNSTimeoutMilliseconds;
  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;
  static const uint16_t kRemotePort;
  // Time to wait before testing successful data transfer / disconnect after
  // request is made on the device.
  static const int kTCPStateUpdateWaitMilliseconds;

  // Callback for DnsClient
  void GetDNSResult(const Error& error, const IPAddress& ip);
  void GarbageCollectDNSClients();

  // Start a new AsyncConnection with callback set to OnConnectionComplete().
  void NextHealthCheckSample();
  void ReportResult();

  // Callback for AsyncConnection.
  // Observe the setup connection to test health state
  void OnConnectionComplete(bool success, int sock_fd);

  void VerifySentData();
  bool GetSocketInfo(int sock_fd, SocketInfo* sock_info);

  void SetSocketDescriptor(int sock_fd);
  void ClearSocketDescriptor();

  // The connection on which the health check is being run.
  ConnectionRefPtr connection_;
  EventDispatcher* dispatcher_;
  // Set of IPs to create TCP connection with for the health check.
  IPAddressStore* remote_ips_;
  base::Callback<void(Result)> result_callback_;

  std::unique_ptr<Sockets> socket_;
  base::WeakPtrFactory<ConnectionHealthChecker> weak_ptr_factory_;

  // Callback passed to |tcp_connection_| to report an established TCP
  // connection.
  const base::Callback<void(bool, int)> connection_complete_callback_;
  // Active TCP connection during health check.
  std::unique_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.
  std::unique_ptr<SocketInfoReader> socket_info_reader_;

  DNSClientFactory* dns_client_factory_;
  ScopedVector<DNSClient> dns_clients_;
  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_t 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_;
  // Number of connection failures in currently active health check.
  int16_t num_connection_failures_;
  // Number of times we have checked the tx-queue for the current send attempt.
  int16_t num_tx_queue_polling_attempts_;
  // Number of out of credit scenarios detected in current health check.
  int16_t num_congested_queue_detected_;
  // Number of successful send attempts currently active health check.
  int16_t num_successful_sends_;

  // Snooze time while polling for updated /proc/tcpinfo
  int tcp_state_update_wait_milliseconds_;

  // Temporarily store the result of health check so that |report_result_|
  // can report it.
  Result health_check_result_;

  DISALLOW_COPY_AND_ASSIGN(ConnectionHealthChecker);
};

}  // namespace shill

#endif  // SHILL_CONNECTION_HEALTH_CHECKER_H_