//
// 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_CONNECTIVITY_TRIAL_H_
#define SHILL_CONNECTIVITY_TRIAL_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/http_request.h"
#include "shill/http_url.h"
#include "shill/net/shill_time.h"
#include "shill/net/sockets.h"
#include "shill/refptr_types.h"
namespace shill {
class ByteString;
class EventDispatcher;
class Time;
// The ConnectivityTrial class implements a single portal detection
// trial. Each trial checks if a connection has "general internet
// connectivity."
//
// ConnectivityTrial is responsible for managing the callbacks between the
// calling class requesting a connectivity trial and the HTTPRequest that is
// used to test connectivity. ConnectivityTrial maps between the HTTPRequest
// response codes to higher-level connection-oriented status.
//
// ConnectivityTrial tests the connection by attempting to parse and access a
// given URL. Any result that deviates from the expected behavior (DNS or HTTP
// errors, as well as retrieved content errors, and timeouts) are considered
// failures.
class ConnectivityTrial {
public:
enum Phase {
kPhaseConnection,
kPhaseDNS,
kPhaseHTTP,
kPhaseContent,
kPhaseUnknown
};
enum Status {
kStatusFailure,
kStatusSuccess,
kStatusTimeout
};
struct Result {
Result()
: phase(kPhaseUnknown), status(kStatusFailure) {}
Result(Phase phase_in, Status status_in)
: phase(phase_in), status(status_in) {}
Phase phase;
Status status;
};
static const char kDefaultURL[];
static const char kResponseExpected[];
ConnectivityTrial(ConnectionRefPtr connection,
EventDispatcher* dispatcher,
int trial_timeout_seconds,
const base::Callback<void(Result)>& trial_callback);
virtual ~ConnectivityTrial();
// Static method used to map a portal detection phase tp a string. This
// includes the phases for connection, DNS, HTTP, returned content and
// unknown.
static const std::string PhaseToString(Phase phase);
// Static method to map from the result of a portal detection phase to a
// status string. This method supports success, timeout and failure.
static const std::string StatusToString(Status status);
// Static method mapping from HTTPRequest responses to ConntectivityTrial
// phases for portal detection. For example, if the HTTPRequest result is
// HTTPRequest::kResultDNSFailure, this method returns a
// ConnectivityTrial::Result with the phase set to
// ConnectivityTrial::kPhaseDNS and the status set to
// ConnectivityTrial::kStatusFailure.
static Result GetPortalResultForRequestResult(HTTPRequest::Result result);
// Start a ConnectivityTrial with the supplied URL and starting delay (ms).
// Returns trus if |url_string| correctly parses as a URL. Returns false (and
// does not start) if the |url_string| fails to parse.
//
// After a trial completes, the callback supplied in the constructor is
// called.
virtual bool Start(const std::string& url_string,
int start_delay_milliseconds);
// After a trial completes, the calling class may call Retry on the trial.
// This allows the underlying HTTPRequest object to be reused. The URL is not
// reparsed and the original URL supplied in the Start command is used. The
// |start_delay| is the time (ms) to wait before starting the trial. Retry
// returns true if the underlying HTTPRequest is still available. If the
// HTTPRequest was reset or never created, Retry will return false.
virtual bool Retry(int start_delay_milliseconds);
// End the current attempt if one is in progress. Will not call the callback
// with any intermediate results.
// The ConnectivityTrial will cancel any existing scheduled tasks and destroy
// the underlying HTTPRequest.
virtual void Stop();
// Method to return if the connection is being actively tested.
virtual bool IsActive();
private:
friend class PortalDetectorTest;
FRIEND_TEST(PortalDetectorTest, StartAttemptFailed);
FRIEND_TEST(PortalDetectorTest, StartAttemptRepeated);
FRIEND_TEST(PortalDetectorTest, StartAttemptAfterDelay);
FRIEND_TEST(PortalDetectorTest, AttemptCount);
FRIEND_TEST(PortalDetectorTest, ReadBadHeadersRetry);
FRIEND_TEST(PortalDetectorTest, ReadBadHeader);
friend class ConnectivityTrialTest;
FRIEND_TEST(ConnectivityTrialTest, StartAttemptFailed);
FRIEND_TEST(ConnectivityTrialTest, TrialRetry);
FRIEND_TEST(ConnectivityTrialTest, ReadBadHeadersRetry);
FRIEND_TEST(ConnectivityTrialTest, IsActive);
// Start a ConnectivityTrial with the supplied delay in ms.
void StartTrialAfterDelay(int start_delay_milliseconds);
// Internal method used to start the actual connectivity trial, called after
// the start delay completes.
void StartTrialTask();
// Callback used to return data read from the HTTPRequest.
void RequestReadCallback(const ByteString& response_data);
// Callback used to return the result of the HTTPRequest.
void RequestResultCallback(HTTPRequest::Result result,
const ByteString& response_data);
// Internal method used to clean up state and call the original caller that
// created and triggered this ConnectivityTrial.
void CompleteTrial(Result result);
// Internal method used to cancel the timeout timer and stop an active
// HTTPRequest. If |reset_request| is true, this method resets the underlying
// HTTPRequest object.
void CleanupTrial(bool reset_request);
// Callback used to cancel the underlying HTTPRequest in the event of a
// timeout.
void TimeoutTrialTask();
ConnectionRefPtr connection_;
EventDispatcher* dispatcher_;
int trial_timeout_seconds_;
base::Callback<void(Result)> trial_callback_;
base::WeakPtrFactory<ConnectivityTrial> weak_ptr_factory_;
base::Callback<void(const ByteString&)> request_read_callback_;
base::Callback<void(HTTPRequest::Result, const ByteString&)>
request_result_callback_;
std::unique_ptr<HTTPRequest> request_;
Sockets sockets_;
HTTPURL url_;
base::CancelableClosure trial_;
base::CancelableClosure trial_timeout_;
bool is_active_;
};
} // namespace shill
#endif // SHILL_CONNECTIVITY_TRIAL_H_