1//
2// Copyright (C) 2012 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17#ifndef SHILL_PORTAL_DETECTOR_H_
18#define SHILL_PORTAL_DETECTOR_H_
19
20#include <memory>
21#include <string>
22#include <vector>
23
24#include <base/callback.h>
25#include <base/cancelable_callback.h>
26#include <base/memory/ref_counted.h>
27#include <base/memory/weak_ptr.h>
28#include <gtest/gtest_prod.h>  // for FRIEND_TEST
29
30#include "shill/connectivity_trial.h"
31#include "shill/net/shill_time.h"
32#include "shill/refptr_types.h"
33
34namespace shill {
35
36class ByteString;
37class PortalDetector;
38class Time;
39
40// The PortalDetector class implements the portal detection
41// facility in shill, which is responsible for checking to see
42// if a connection has "general internet connectivity".
43//
44// This information can be used for ranking one connection
45// against another, or for informing UI and other components
46// outside the connection manager whether the connection seems
47// available for "general use" or if further user action may be
48// necessary (e.g, click through of a WiFi Hotspot's splash
49// page).
50//
51// This is achieved by using one or more ConnectivityTrial attempts
52// to access a URL and expecting a specific response.  Any result
53// that deviates from this result (DNS or HTTP errors, as well as
54// deviations from the expected content) are considered failures.
55class PortalDetector {
56 public:
57  struct Result {
58    Result()
59        : trial_result(ConnectivityTrial::Result()),
60          num_attempts(0),
61          final(false) {}
62    explicit Result(ConnectivityTrial::Result result_in)
63        : trial_result(result_in),
64          num_attempts(0),
65          final(false) {}
66    Result(ConnectivityTrial::Result result_in,
67           int num_attempts_in,
68           int final_in)
69        : trial_result(result_in),
70          num_attempts(num_attempts_in),
71          final(final_in) {}
72
73    ConnectivityTrial::Result trial_result;
74
75    // Total number of connectivity trials attempted.
76    // This includes failure, timeout and successful attempts.
77    // This only valid when |final| is true.
78    int num_attempts;
79    bool final;
80  };
81
82  static const int kDefaultCheckIntervalSeconds;
83  static const char kDefaultCheckPortalList[];
84  // Maximum number of times the PortalDetector will attempt a connection.
85  static const int kMaxRequestAttempts;
86
87  PortalDetector(ConnectionRefPtr connection,
88                 EventDispatcher* dispatcher,
89                 const base::Callback<void(const PortalDetector::Result&)>
90                     &callback);
91  virtual ~PortalDetector();
92
93  // Start a portal detection test.  Returns true if |url_string| correctly
94  // parses as a URL.  Returns false (and does not start) if the |url_string|
95  // fails to parse.
96  //
97  // As each attempt completes the callback handed to the constructor will
98  // be called.  The PortalDetector will try up to kMaxRequestAttempts times
99  // to successfully retrieve the URL.  If the attempt is successful or
100  // this is the last attempt, the "final" flag in the Result structure will
101  // be true, otherwise it will be false, and the PortalDetector will
102  // schedule the next attempt.
103  virtual bool Start(const std::string& url_string);
104  virtual bool StartAfterDelay(const std::string& url_string,
105                               int delay_seconds);
106
107  // End the current portal detection process if one exists, and do not call
108  // the callback.
109  virtual void Stop();
110
111  // Returns whether portal request is "in progress": whether the underlying
112  // ConnectivityTrial is in the progress of making attempts.  Returns true if
113  // attempts are in progress, false otherwise.  Notably, this function
114  // returns false during the period of time between calling "Start" or
115  // "StartAfterDelay" and the actual start of the first attempt. In the case
116  // where multiple attempts may be tried, IsInProgress will return true after
117  // the first attempt has actively started testing the connection.
118  virtual bool IsInProgress();
119
120 private:
121  friend class PortalDetectorTest;
122  FRIEND_TEST(PortalDetectorTest, StartAttemptFailed);
123  FRIEND_TEST(PortalDetectorTest, AdjustStartDelayImmediate);
124  FRIEND_TEST(PortalDetectorTest, AdjustStartDelayAfterDelay);
125  FRIEND_TEST(PortalDetectorTest, AttemptCount);
126  FRIEND_TEST(PortalDetectorTest, ReadBadHeadersRetry);
127  FRIEND_TEST(PortalDetectorTest, ReadBadHeader);
128  FRIEND_TEST(PortalDetectorTest, RequestTimeout);
129  FRIEND_TEST(PortalDetectorTest, ReadPartialHeaderTimeout);
130  FRIEND_TEST(PortalDetectorTest, ReadCompleteHeader);
131  FRIEND_TEST(PortalDetectorTest, ReadMatchingHeader);
132  FRIEND_TEST(PortalDetectorTest, InvalidURL);
133
134  // Minimum time between attempts to connect to server.
135  static const int kMinTimeBetweenAttemptsSeconds;
136  // Time to wait for request to complete.
137  static const int kRequestTimeoutSeconds;
138  // Maximum number of failures in content phase before we stop attempting
139  // connections.
140  static const int kMaxFailuresInContentPhase;
141
142  // Internal method to update the start time of the next event.  This is used
143  // to keep attempts spaced by at least kMinTimeBetweenAttemptsSeconds in the
144  // event of a retry.
145  void UpdateAttemptTime(int delay_seconds);
146
147  // Internal method used to adjust the start delay in the event of a retry.
148  // Calculates the elapsed time between the most recent attempt and the point
149  // the retry is scheduled.  Adds an additional delay to meet the
150  // kMinTimeBetweenAttemptsSeconds requirement.
151  int AdjustStartDelay(int init_delay_seconds);
152
153  // Callback used by ConnectivityTrial to return |result| after attempting to
154  // determine connectivity status.
155  void CompleteAttempt(ConnectivityTrial::Result result);
156
157  int attempt_count_;
158  struct timeval attempt_start_time_;
159  ConnectionRefPtr connection_;
160  EventDispatcher* dispatcher_;
161  base::WeakPtrFactory<PortalDetector> weak_ptr_factory_;
162  base::Callback<void(const Result&)> portal_result_callback_;
163  base::Callback<void(ConnectivityTrial::Result)> connectivity_trial_callback_;
164  Time* time_;
165  int failures_in_content_phase_;
166  std::unique_ptr<ConnectivityTrial> connectivity_trial_;
167
168
169  DISALLOW_COPY_AND_ASSIGN(PortalDetector);
170};
171
172}  // namespace shill
173
174#endif  // SHILL_PORTAL_DETECTOR_H_
175