blob: 6ad53b2a372acbdd1d5ec378d9ef2d05bd0bb260 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_CAPTIVE_PORTAL_CAPTIVE_PORTAL_SERVICE_H_
#define CHROME_BROWSER_CAPTIVE_PORTAL_CAPTIVE_PORTAL_SERVICE_H_
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/threading/non_thread_safe.h"
#include "base/time.h"
#include "base/timer.h"
#include "chrome/browser/api/prefs/pref_member.h"
#include "chrome/browser/profiles/profile_keyed_service.h"
#include "content/public/browser/notification_observer.h"
#include "googleurl/src/gurl.h"
#include "net/base/backoff_entry.h"
#include "net/url_request/url_fetcher_delegate.h"
class Profile;
namespace net {
class URLFetcher;
}
namespace captive_portal {
// Possible results of an attempt to detect a captive portal.
enum Result {
// There's a confirmed connection to the Internet.
RESULT_INTERNET_CONNECTED,
// The URL request received a network or HTTP error, or a non-HTTP response.
RESULT_NO_RESPONSE,
// The URL request apparently encountered a captive portal. It received a
// a valid HTTP response with a 2xx, 3xx, or 511 status code, other than 204.
RESULT_BEHIND_CAPTIVE_PORTAL,
RESULT_COUNT
};
// Service that checks for captive portals when queried, and sends a
// NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT with the Profile as the source and
// a CaptivePortalService::Results as the details.
//
// Captive portal checks are rate-limited. The CaptivePortalService may only
// be accessed on the UI thread.
// Design doc: https://docs.google.com/document/d/1k-gP2sswzYNvryu9NcgN7q5XrsMlUdlUdoW9WRaEmfM/edit
class CaptivePortalService : public ProfileKeyedService,
public net::URLFetcherDelegate,
public content::NotificationObserver,
public base::NonThreadSafe {
public:
enum TestingState {
NOT_TESTING,
DISABLED_FOR_TESTING, // The service is always disabled.
SKIP_OS_CHECK_FOR_TESTING // The service can be enabled even if the OS has
// native captive portal detection.
};
// The details sent via a NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT.
struct Results {
// The result of the second most recent captive portal check.
Result previous_result;
// The result of the most recent captive portal check.
Result result;
};
explicit CaptivePortalService(Profile* profile);
virtual ~CaptivePortalService();
// Triggers a check for a captive portal. If there's already a check in
// progress, does nothing. Throttles the rate at which requests are sent.
// Always sends the result notification asynchronously.
void DetectCaptivePortal();
// Returns the URL used for captive portal testing. When a captive portal is
// detected, this URL will take us to the captive portal landing page.
const GURL& test_url() const { return test_url_; }
// Result of the most recent captive portal check.
Result last_detection_result() const { return last_detection_result_; }
// Whether or not the CaptivePortalService is enabled. When disabled, all
// checks return INTERNET_CONNECTED.
bool enabled() const { return enabled_; }
// Used to disable captive portal detection so it doesn't interfere with
// tests. Should be called before the service is created.
static void set_state_for_testing(TestingState testing_state) {
testing_state_ = testing_state;
}
static TestingState get_state_for_testing() { return testing_state_; }
private:
friend class CaptivePortalServiceTest;
friend class CaptivePortalBrowserTest;
// Subclass of BackoffEntry that uses the CaptivePortalService's
// GetCurrentTime function, for unit testing.
class RecheckBackoffEntry;
enum State {
// No check is running or pending.
STATE_IDLE,
// The timer to check for a captive portal is running.
STATE_TIMER_RUNNING,
// There's an outstanding HTTP request to check for a captive portal.
STATE_CHECKING_FOR_PORTAL,
};
// Contains all the information about the minimum time allowed between two
// consecutive captive portal checks.
struct RecheckPolicy {
// Constructor initializes all values to defaults.
RecheckPolicy();
// The minimum amount of time between two captive portal checks, when the
// last check found no captive portal.
int initial_backoff_no_portal_ms;
// The minimum amount of time between two captive portal checks, when the
// last check found a captive portal. This is expected to be less than
// |initial_backoff_no_portal_ms|. Also used when the service is disabled.
int initial_backoff_portal_ms;
net::BackoffEntry::Policy backoff_policy;
};
// Initiates a captive portal check, without any throttling. If the service
// is disabled, just acts like there's an Internet connection.
void DetectCaptivePortalInternal();
// net::URLFetcherDelegate:
virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
// content::NotificationObserver:
virtual void Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) OVERRIDE;
// ProfileKeyedService:
virtual void Shutdown() OVERRIDE;
// Called when a captive portal check completes. Passes the result to all
// observers.
void OnResult(Result result);
// Updates BackoffEntry::Policy and creates a new BackoffEntry, which
// resets the count used for throttling.
void ResetBackoffEntry(Result result);
// Updates |enabled_| based on command line flags and Profile preferences,
// and sets |state_| to STATE_NONE if it's false.
// TODO(mmenke): Figure out on which platforms, if any, should not use
// automatic captive portal detection. Currently it's enabled
// on all platforms, though this code is not compiled on
// Android, since it lacks the Browser class.
void UpdateEnabledState();
// Takes a net::URLFetcher that has finished trying to retrieve the test
// URL, and returns a captive_portal::Result based on its result.
// If the response is a 503 with a Retry-After header, |retry_after| is
// populated accordingly. Otherwise, it's set to base::TimeDelta().
Result GetCaptivePortalResultFromResponse(const net::URLFetcher* url_fetcher,
base::TimeDelta* retry_after) const;
// Returns the current time. Used only when determining time until a
// Retry-After date. Overridden by unit tests.
virtual base::Time GetCurrentTime() const;
// Returns the current TimeTicks. Overridden by unit tests.
virtual base::TimeTicks GetCurrentTimeTicks() const;
// Returns true if a captive portal check is currently running.
bool FetchingURL() const;
// Returns true if the timer to try and detect a captive portal is running.
bool TimerRunning() const;
State state() const { return state_; }
RecheckPolicy& recheck_policy() { return recheck_policy_; }
void set_test_url(const GURL& test_url) { test_url_ = test_url; }
// The profile that owns this CaptivePortalService.
Profile* profile_;
State state_;
// True if the service is enabled. When not enabled, all checks will return
// RESULT_INTERNET_CONNECTED.
bool enabled_;
// The result of the most recent captive portal check.
Result last_detection_result_;
// Number of sequential checks with the same captive portal result.
int num_checks_with_same_result_;
// Time when |last_detection_result_| was first received.
base::TimeTicks first_check_time_with_same_result_;
// Time the last captive portal check completed.
base::TimeTicks last_check_time_;
// Policy for throttling portal checks.
RecheckPolicy recheck_policy_;
// Implements behavior needed by |recheck_policy_|. Whenever there's a new
// captive_portal::Result, BackoffEntry::Policy is updated and
// |backoff_entry_| is recreated. Each check that returns the same Result
// is considered a "failure", to trigger throttling.
scoped_ptr<net::BackoffEntry> backoff_entry_;
scoped_ptr<net::URLFetcher> url_fetcher_;
// URL that returns a 204 response code when connected to the Internet.
GURL test_url_;
// The pref member for whether navigation errors should be resolved with a web
// service. Actually called "alternate_error_pages", since it's also used for
// the Link Doctor.
BooleanPrefMember resolve_errors_with_web_service_;
base::OneShotTimer<CaptivePortalService> check_captive_portal_timer_;
static TestingState testing_state_;
DISALLOW_COPY_AND_ASSIGN(CaptivePortalService);
};
} // namespace captive_portal
#endif // CHROME_BROWSER_CAPTIVE_PORTAL_CAPTIVE_PORTAL_SERVICE_H_