C++程序  |  175行  |  6.61 KB

//
// 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_