blob: 4628cc3f9afdb02cc4e37150e01afb5bae7a81bc [file] [log] [blame]
// 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
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include <string>
#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 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 {
struct Result {
: trial_result(ConnectivityTrial::Result()),
final(false) {}
explicit Result(ConnectivityTrial::Result result_in)
: trial_result(result_in),
final(false) {}
Result(ConnectivityTrial::Result result_in,
int num_attempts_in,
int final_in)
: trial_result(result_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&)>
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();
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_;
Time* time_;
int failures_in_content_phase_;
std::unique_ptr<ConnectivityTrial> connectivity_trial_;
} // namespace shill