summaryrefslogtreecommitdiff
path: root/connection_health_checker.h
blob: 791d7a5fda80399bb0d04466a70e491dae5ae364 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
//
// 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_