summaryrefslogtreecommitdiff
path: root/portal_detector.h
blob: ab28bb002c8d349797fd23f324183023f3e52f78 (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
//
// Copyright (C) 2012 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_PORTAL_DETECTOR_H_
#define SHILL_PORTAL_DETECTOR_H_

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

#include <base/callback.h>
#include <base/cancelable_callback.h>
#include <base/memory/ref_counted.h>
#include <base/memory/weak_ptr.h>
#include <gtest/gtest_prod.h>  // for FRIEND_TEST

#include "shill/connectivity_trial.h"
#include "shill/net/shill_time.h"
#include "shill/refptr_types.h"

namespace shill {

class ByteString;
class PortalDetector;
class Time;

// The PortalDetector class implements the portal detection
// facility in shill, which is responsible for checking to see
// if a connection has "general internet connectivity".
//
// This information can be used for ranking one connection
// against another, or for informing UI and other components
// outside the connection manager whether the connection seems
// available for "general use" or if further user action may be
// necessary (e.g, click through of a WiFi Hotspot's splash
// page).
//
// This is achieved by using one or more ConnectivityTrial attempts
// to access a URL and expecting a specific response.  Any result
// that deviates from this result (DNS or HTTP errors, as well as
// deviations from the expected content) are considered failures.
class PortalDetector {
 public:
  struct Result {
    Result()
        : trial_result(ConnectivityTrial::Result()),
          num_attempts(0),
          final(false) {}
    explicit Result(ConnectivityTrial::Result result_in)
        : trial_result(result_in),
          num_attempts(0),
          final(false) {}
    Result(ConnectivityTrial::Result result_in,
           int num_attempts_in,
           int final_in)
        : trial_result(result_in),
          num_attempts(num_attempts_in),
          final(final_in) {}

    ConnectivityTrial::Result trial_result;

    // Total number of connectivity trials attempted.
    // This includes failure, timeout and successful attempts.
    // This only valid when |final| is true.
    int num_attempts;
    bool final;
  };

  static const int kDefaultCheckIntervalSeconds;
  static const char kDefaultCheckPortalList[];
  // Maximum number of times the PortalDetector will attempt a connection.
  static const int kMaxRequestAttempts;

  PortalDetector(ConnectionRefPtr connection,
                 EventDispatcher* dispatcher,
                 const base::Callback<void(const PortalDetector::Result&)>
                     &callback);
  virtual ~PortalDetector();

  // Start a portal detection test.  Returns true if |url_string| correctly
  // parses as a URL.  Returns false (and does not start) if the |url_string|
  // fails to parse.
  //
  // As each attempt completes the callback handed to the constructor will
  // be called.  The PortalDetector will try up to kMaxRequestAttempts times
  // to successfully retrieve the URL.  If the attempt is successful or
  // this is the last attempt, the "final" flag in the Result structure will
  // be true, otherwise it will be false, and the PortalDetector will
  // schedule the next attempt.
  virtual bool Start(const std::string& url_string);
  virtual bool StartAfterDelay(const std::string& url_string,
                               int delay_seconds);

  // End the current portal detection process if one exists, and do not call
  // the callback.
  virtual void Stop();

  // Returns whether portal request is "in progress": whether the underlying
  // ConnectivityTrial is in the progress of making attempts.  Returns true if
  // attempts are in progress, false otherwise.  Notably, this function
  // returns false during the period of time between calling "Start" or
  // "StartAfterDelay" and the actual start of the first attempt. In the case
  // where multiple attempts may be tried, IsInProgress will return true after
  // the first attempt has actively started testing the connection.
  virtual bool IsInProgress();

 private:
  friend class PortalDetectorTest;
  FRIEND_TEST(PortalDetectorTest, StartAttemptFailed);
  FRIEND_TEST(PortalDetectorTest, AdjustStartDelayImmediate);
  FRIEND_TEST(PortalDetectorTest, AdjustStartDelayAfterDelay);
  FRIEND_TEST(PortalDetectorTest, AttemptCount);
  FRIEND_TEST(PortalDetectorTest, ReadBadHeadersRetry);
  FRIEND_TEST(PortalDetectorTest, ReadBadHeader);
  FRIEND_TEST(PortalDetectorTest, RequestTimeout);
  FRIEND_TEST(PortalDetectorTest, ReadPartialHeaderTimeout);
  FRIEND_TEST(PortalDetectorTest, ReadCompleteHeader);
  FRIEND_TEST(PortalDetectorTest, ReadMatchingHeader);
  FRIEND_TEST(PortalDetectorTest, InvalidURL);

  // Minimum time between attempts to connect to server.
  static const int kMinTimeBetweenAttemptsSeconds;
  // Time to wait for request to complete.
  static const int kRequestTimeoutSeconds;
  // Maximum number of failures in content phase before we stop attempting
  // connections.
  static const int kMaxFailuresInContentPhase;

  // Internal method to update the start time of the next event.  This is used
  // to keep attempts spaced by at least kMinTimeBetweenAttemptsSeconds in the
  // event of a retry.
  void UpdateAttemptTime(int delay_seconds);

  // Internal method used to adjust the start delay in the event of a retry.
  // Calculates the elapsed time between the most recent attempt and the point
  // the retry is scheduled.  Adds an additional delay to meet the
  // kMinTimeBetweenAttemptsSeconds requirement.
  int AdjustStartDelay(int init_delay_seconds);

  // Callback used by ConnectivityTrial to return |result| after attempting to
  // determine connectivity status.
  void CompleteAttempt(ConnectivityTrial::Result result);

  int attempt_count_;
  struct timeval attempt_start_time_;
  ConnectionRefPtr connection_;
  EventDispatcher* dispatcher_;
  base::WeakPtrFactory<PortalDetector> weak_ptr_factory_;
  base::Callback<void(const Result&)> portal_result_callback_;
  base::Callback<void(ConnectivityTrial::Result)> connectivity_trial_callback_;
  Time* time_;
  int failures_in_content_phase_;
  std::unique_ptr<ConnectivityTrial> connectivity_trial_;


  DISALLOW_COPY_AND_ASSIGN(PortalDetector);
};

}  // namespace shill

#endif  // SHILL_PORTAL_DETECTOR_H_