blob: dfa2b7ba11ed2a508da62936b331ec5a2dba1b94 [file] [log] [blame]
// Copyright (c) 2012 The Chromium OS 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 SHILL_PORTAL_DETECTOR_
#define SHILL_PORTAL_DETECTOR_
#include <string>
#include <vector>
#include <base/callback.h>
#include <base/cancelable_callback.h>
#include <base/memory/ref_counted.h>
#include <base/memory/scoped_ptr.h>
#include <base/memory/weak_ptr.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
#include "shill/http_request.h"
#include "shill/http_url.h"
#include "shill/refptr_types.h"
#include "shill/shill_time.h"
#include "shill/sockets.h"
namespace shill {
class ByteString;
class EventDispatcher;
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 trying 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:
enum Phase {
kPhaseConnection,
kPhaseDNS,
kPhaseHTTP,
kPhaseContent,
kPhaseUnknown
};
enum Status {
kStatusFailure,
kStatusSuccess,
kStatusTimeout
};
struct Result {
Result()
: phase(kPhaseUnknown), status(kStatusFailure),
num_attempts(0), final(false) {}
Result(Phase phase_in, Status status_in)
: phase(phase_in), status(status_in),
num_attempts(0), final(false) {}
Result(Phase phase_in, Status status_in, int num_attempts_in, bool final_in)
: phase(phase_in), status(status_in),
num_attempts(num_attempts_in), final(final_in) {}
Phase phase;
Status status;
// Total number of portal detections 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[];
static const char kDefaultURL[];
static const char kResponseExpected[];
// Maximum number of times the PortalDetector will attempt a connection.
static const int kMaxRequestAttempts;
PortalDetector(ConnectionRefPtr connection,
EventDispatcher *dispatcher,
const base::Callback<void(const 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 portal
// detector 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.
virtual bool IsInProgress();
static const std::string PhaseToString(Phase phase);
static const std::string StatusToString(Status status);
static Result GetPortalResultForRequestResult(HTTPRequest::Result result);
private:
friend class PortalDetectorTest;
FRIEND_TEST(PortalDetectorTest, StartAttemptFailed);
FRIEND_TEST(PortalDetectorTest, StartAttemptRepeated);
FRIEND_TEST(PortalDetectorTest, StartAttemptAfterDelay);
FRIEND_TEST(PortalDetectorTest, AttemptCount);
FRIEND_TEST(PortalDetectorTest, ReadBadHeadersRetry);
// 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;
static const char kPhaseConnectionString[];
static const char kPhaseDNSString[];
static const char kPhaseHTTPString[];
static const char kPhaseContentString[];
static const char kPhaseUnknownString[];
static const char kStatusFailureString[];
static const char kStatusSuccessString[];
static const char kStatusTimeoutString[];
void CompleteAttempt(Result result);
void RequestReadCallback(const ByteString &response_data);
void RequestResultCallback(HTTPRequest::Result result,
const ByteString &response_data);
void StartAttempt(int init_delay_seconds);
void StartAttemptTask();
void StopAttempt();
void TimeoutAttemptTask();
int attempt_count_;
struct timeval attempt_start_time_;
ConnectionRefPtr connection_;
EventDispatcher *dispatcher_;
base::WeakPtrFactory<PortalDetector> weak_ptr_factory_;
base::CancelableClosure attempt_timeout_;
base::CancelableClosure start_attempt_;
base::Callback<void(const Result &)> portal_result_callback_;
scoped_ptr<HTTPRequest> request_;
base::Callback<void(const ByteString &)> request_read_callback_;
base::Callback<void(HTTPRequest::Result, const ByteString &)>
request_result_callback_;
Sockets sockets_;
Time *time_;
HTTPURL url_;
int failures_in_content_phase_;
DISALLOW_COPY_AND_ASSIGN(PortalDetector);
};
} // namespace shill
#endif // SHILL_PORTAL_DETECTOR_