1 //
2 // Copyright (C) 2012 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #ifndef SHILL_CONNECTIVITY_TRIAL_H_
18 #define SHILL_CONNECTIVITY_TRIAL_H_
19 
20 #include <memory>
21 #include <string>
22 #include <vector>
23 
24 #include <base/callback.h>
25 #include <base/cancelable_callback.h>
26 #include <base/memory/ref_counted.h>
27 #include <base/memory/weak_ptr.h>
28 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
29 
30 #include "shill/http_request.h"
31 #include "shill/http_url.h"
32 #include "shill/net/shill_time.h"
33 #include "shill/net/sockets.h"
34 #include "shill/refptr_types.h"
35 
36 namespace shill {
37 
38 class ByteString;
39 class EventDispatcher;
40 class Time;
41 
42 // The ConnectivityTrial class implements a single portal detection
43 // trial.  Each trial checks if a connection has "general internet
44 // connectivity."
45 //
46 // ConnectivityTrial is responsible for managing the callbacks between the
47 // calling class requesting a connectivity trial and the HTTPRequest that is
48 // used to test connectivity.  ConnectivityTrial maps between the HTTPRequest
49 // response codes to higher-level connection-oriented status.
50 //
51 // ConnectivityTrial tests the connection by attempting to parse and access a
52 // given URL.  Any result that deviates from the expected behavior (DNS or HTTP
53 // errors, as well as retrieved content errors, and timeouts) are considered
54 // failures.
55 
56 class ConnectivityTrial {
57  public:
58   enum Phase {
59     kPhaseConnection,
60     kPhaseDNS,
61     kPhaseHTTP,
62     kPhaseContent,
63     kPhaseUnknown
64   };
65 
66   enum Status {
67     kStatusFailure,
68     kStatusSuccess,
69     kStatusTimeout
70   };
71 
72   struct Result {
ResultResult73     Result()
74         : phase(kPhaseUnknown), status(kStatusFailure) {}
ResultResult75     Result(Phase phase_in, Status status_in)
76         : phase(phase_in), status(status_in) {}
77     Phase phase;
78     Status status;
79   };
80 
81   static const char kDefaultURL[];
82   static const char kResponseExpected[];
83 
84   ConnectivityTrial(ConnectionRefPtr connection,
85                     EventDispatcher* dispatcher,
86                     int trial_timeout_seconds,
87                     const base::Callback<void(Result)>& trial_callback);
88   virtual ~ConnectivityTrial();
89 
90   // Static method used to map a portal detection phase tp a string.  This
91   // includes the phases for connection, DNS, HTTP, returned content and
92   // unknown.
93   static const std::string PhaseToString(Phase phase);
94 
95   // Static method to map from the result of a portal detection phase to a
96   // status string. This method supports success, timeout and failure.
97   static const std::string StatusToString(Status status);
98 
99   // Static method mapping from HTTPRequest responses to ConntectivityTrial
100   // phases for portal detection. For example, if the HTTPRequest result is
101   // HTTPRequest::kResultDNSFailure, this method returns a
102   // ConnectivityTrial::Result with the phase set to
103   // ConnectivityTrial::kPhaseDNS and the status set to
104   // ConnectivityTrial::kStatusFailure.
105   static Result GetPortalResultForRequestResult(HTTPRequest::Result result);
106 
107   // Start a ConnectivityTrial with the supplied URL and starting delay (ms).
108   // Returns trus if |url_string| correctly parses as a URL.  Returns false (and
109   // does not start) if the |url_string| fails to parse.
110   //
111   // After a trial completes, the callback supplied in the constructor is
112   // called.
113   virtual bool Start(const std::string& url_string,
114                      int start_delay_milliseconds);
115 
116   // After a trial completes, the calling class may call Retry on the trial.
117   // This allows the underlying HTTPRequest object to be reused.  The URL is not
118   // reparsed and the original URL supplied in the Start command is used.  The
119   // |start_delay| is the time (ms) to wait before starting the trial.  Retry
120   // returns true if the underlying HTTPRequest is still available.  If the
121   // HTTPRequest was reset or never created, Retry will return false.
122   virtual bool Retry(int start_delay_milliseconds);
123 
124   // End the current attempt if one is in progress.  Will not call the callback
125   // with any intermediate results.
126   // The ConnectivityTrial will cancel any existing scheduled tasks and destroy
127   // the underlying HTTPRequest.
128   virtual void Stop();
129 
130   // Method to return if the connection is being actively tested.
131   virtual bool IsActive();
132 
133  private:
134   friend class PortalDetectorTest;
135   FRIEND_TEST(PortalDetectorTest, StartAttemptFailed);
136   FRIEND_TEST(PortalDetectorTest, StartAttemptRepeated);
137   FRIEND_TEST(PortalDetectorTest, StartAttemptAfterDelay);
138   FRIEND_TEST(PortalDetectorTest, AttemptCount);
139   FRIEND_TEST(PortalDetectorTest, ReadBadHeadersRetry);
140   FRIEND_TEST(PortalDetectorTest, ReadBadHeader);
141   friend class ConnectivityTrialTest;
142   FRIEND_TEST(ConnectivityTrialTest, StartAttemptFailed);
143   FRIEND_TEST(ConnectivityTrialTest, TrialRetry);
144   FRIEND_TEST(ConnectivityTrialTest, ReadBadHeadersRetry);
145   FRIEND_TEST(ConnectivityTrialTest, IsActive);
146 
147   // Start a ConnectivityTrial with the supplied delay in ms.
148   void StartTrialAfterDelay(int start_delay_milliseconds);
149 
150   // Internal method used to start the actual connectivity trial, called after
151   // the start delay completes.
152   void StartTrialTask();
153 
154   // Callback used to return data read from the HTTPRequest.
155   void RequestReadCallback(const ByteString& response_data);
156 
157   // Callback used to return the result of the HTTPRequest.
158   void RequestResultCallback(HTTPRequest::Result result,
159                              const ByteString& response_data);
160 
161   // Internal method used to clean up state and call the original caller that
162   // created and triggered this ConnectivityTrial.
163   void CompleteTrial(Result result);
164 
165   // Internal method used to cancel the timeout timer and stop an active
166   // HTTPRequest.  If |reset_request| is true, this method resets the underlying
167   // HTTPRequest object.
168   void CleanupTrial(bool reset_request);
169 
170   // Callback used to cancel the underlying HTTPRequest in the event of a
171   // timeout.
172   void TimeoutTrialTask();
173 
174   ConnectionRefPtr connection_;
175   EventDispatcher* dispatcher_;
176   int trial_timeout_seconds_;
177   base::Callback<void(Result)> trial_callback_;
178   base::WeakPtrFactory<ConnectivityTrial> weak_ptr_factory_;
179   base::Callback<void(const ByteString&)> request_read_callback_;
180   base::Callback<void(HTTPRequest::Result, const ByteString&)>
181         request_result_callback_;
182   std::unique_ptr<HTTPRequest> request_;
183 
184   Sockets sockets_;
185   HTTPURL url_;
186   base::CancelableClosure trial_;
187   base::CancelableClosure trial_timeout_;
188   bool is_active_;
189 };
190 
191 }  // namespace shill
192 
193 #endif  // SHILL_CONNECTIVITY_TRIAL_H_
194