247 lines
9.2 KiB
C++
247 lines
9.2 KiB
C++
//
|
|
// 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_
|