Upstream version 7.35.144.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / captive_portal / captive_portal_detector.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/captive_portal/captive_portal_detector.h"
6
7 #include "base/logging.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "chrome/browser/profiles/profile.h"
10 #include "net/base/load_flags.h"
11 #include "net/http/http_response_headers.h"
12 #include "net/url_request/url_request_status.h"
13
14 namespace captive_portal {
15
16 namespace {
17
18 const char* const kCaptivePortalResultNames[] = {
19   "InternetConnected",
20   "NoResponse",
21   "BehindCaptivePortal",
22   "NumCaptivePortalResults",
23 };
24 COMPILE_ASSERT(arraysize(kCaptivePortalResultNames) == RESULT_COUNT + 1,
25                captive_portal_result_name_count_mismatch);
26
27 }  // namespace
28
29 const char CaptivePortalDetector::kDefaultURL[] =
30     "http://www.gstatic.com/generate_204";
31
32 CaptivePortalDetector::CaptivePortalDetector(
33     const scoped_refptr<net::URLRequestContextGetter>& request_context)
34     : request_context_(request_context) {
35 }
36
37 CaptivePortalDetector::~CaptivePortalDetector() {
38 }
39
40 // static
41 std::string CaptivePortalDetector::CaptivePortalResultToString(Result result) {
42   DCHECK_GE(result, 0);
43   DCHECK_LT(static_cast<unsigned int>(result),
44             arraysize(kCaptivePortalResultNames));
45   return kCaptivePortalResultNames[result];
46 }
47
48 void CaptivePortalDetector::DetectCaptivePortal(
49     const GURL& url,
50     const DetectionCallback& detection_callback) {
51   DCHECK(CalledOnValidThread());
52   DCHECK(!FetchingURL());
53   DCHECK(detection_callback_.is_null());
54
55   detection_callback_ = detection_callback;
56
57   // The first 0 means this can use a TestURLFetcherFactory in unit tests.
58   url_fetcher_.reset(net::URLFetcher::Create(0,
59                                              url,
60                                              net::URLFetcher::GET,
61                                              this));
62   url_fetcher_->SetAutomaticallyRetryOn5xx(false);
63   url_fetcher_->SetRequestContext(request_context_.get());
64
65   // Can't safely use net::LOAD_DISABLE_CERT_REVOCATION_CHECKING here,
66   // since then the connection may be reused without checking the cert.
67   url_fetcher_->SetLoadFlags(
68       net::LOAD_BYPASS_CACHE |
69       net::LOAD_DO_NOT_PROMPT_FOR_LOGIN |
70       net::LOAD_DO_NOT_SAVE_COOKIES |
71       net::LOAD_DO_NOT_SEND_COOKIES |
72       net::LOAD_DO_NOT_SEND_AUTH_DATA);
73   url_fetcher_->Start();
74 }
75
76 void CaptivePortalDetector::Cancel() {
77   url_fetcher_.reset();
78   detection_callback_.Reset();
79 }
80
81 void CaptivePortalDetector::OnURLFetchComplete(const net::URLFetcher* source) {
82   DCHECK(CalledOnValidThread());
83   DCHECK(FetchingURL());
84   DCHECK_EQ(url_fetcher_.get(), source);
85   DCHECK(!detection_callback_.is_null());
86
87   Results results;
88   GetCaptivePortalResultFromResponse(url_fetcher_.get(), &results);
89   DetectionCallback callback = detection_callback_;
90   url_fetcher_.reset();
91   detection_callback_.Reset();
92   callback.Run(results);
93 }
94
95 // Takes a net::URLFetcher that has finished trying to retrieve the test
96 // URL, and returns a CaptivePortalService::Result based on its result.
97 void CaptivePortalDetector::GetCaptivePortalResultFromResponse(
98     const net::URLFetcher* url_fetcher,
99     Results* results) const {
100   DCHECK(results);
101   DCHECK(!url_fetcher->GetStatus().is_io_pending());
102
103   results->result = RESULT_NO_RESPONSE;
104   results->response_code = url_fetcher->GetResponseCode();
105   results->retry_after_delta = base::TimeDelta();
106   results->landing_url = url_fetcher->GetURL();
107
108   // If there's a network error of some sort when fetching a file via HTTP,
109   // there may be a networking problem, rather than a captive portal.
110   // TODO(mmenke):  Consider special handling for redirects that end up at
111   //                errors, especially SSL certificate errors.
112   if (url_fetcher->GetStatus().status() != net::URLRequestStatus::SUCCESS)
113     return;
114
115   // In the case of 503 errors, look for the Retry-After header.
116   if (results->response_code == 503) {
117     net::HttpResponseHeaders* headers = url_fetcher->GetResponseHeaders();
118     std::string retry_after_string;
119
120     // If there's no Retry-After header, nothing else to do.
121     if (!headers->EnumerateHeader(NULL, "Retry-After", &retry_after_string))
122       return;
123
124     // Otherwise, try parsing it as an integer (seconds) or as an HTTP date.
125     int seconds;
126     base::Time full_date;
127     if (base::StringToInt(retry_after_string, &seconds)) {
128       results->retry_after_delta = base::TimeDelta::FromSeconds(seconds);
129     } else if (headers->GetTimeValuedHeader("Retry-After", &full_date)) {
130       base::Time now = GetCurrentTime();
131       if (full_date > now)
132         results->retry_after_delta = full_date - now;
133     }
134     return;
135   }
136
137   // A 511 response (Network Authentication Required) means that the user needs
138   // to login to whatever server issued the response.
139   // See:  http://tools.ietf.org/html/rfc6585
140   if (results->response_code == 511) {
141     results->result = RESULT_BEHIND_CAPTIVE_PORTAL;
142     return;
143   }
144
145   // Other non-2xx/3xx HTTP responses may indicate server errors.
146   if (results->response_code >= 400 || results->response_code < 200)
147     return;
148
149   // A 204 response code indicates there's no captive portal.
150   if (results->response_code == 204) {
151     results->result = RESULT_INTERNET_CONNECTED;
152     return;
153   }
154
155   // Otherwise, assume it's a captive portal.
156   results->result = RESULT_BEHIND_CAPTIVE_PORTAL;
157 }
158
159 base::Time CaptivePortalDetector::GetCurrentTime() const {
160   if (time_for_testing_.is_null())
161     return base::Time::Now();
162   else
163     return time_for_testing_;
164 }
165
166 bool CaptivePortalDetector::FetchingURL() const {
167   return url_fetcher_.get() != NULL;
168 }
169
170 }  // namespace captive_portal