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.
5 #include "components/captive_portal/captive_portal_detector.h"
7 #include "base/logging.h"
8 #include "base/profiler/scoped_profile.h"
9 #include "base/strings/string_number_conversions.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"
14 namespace captive_portal
{
16 const char CaptivePortalDetector::kDefaultURL
[] =
17 "http://www.gstatic.com/generate_204";
19 CaptivePortalDetector::CaptivePortalDetector(
20 const scoped_refptr
<net::URLRequestContextGetter
>& request_context
)
21 : request_context_(request_context
) {
24 CaptivePortalDetector::~CaptivePortalDetector() {
27 void CaptivePortalDetector::DetectCaptivePortal(
29 const DetectionCallback
& detection_callback
) {
30 DCHECK(CalledOnValidThread());
31 DCHECK(!FetchingURL());
32 DCHECK(detection_callback_
.is_null());
34 detection_callback_
= detection_callback
;
36 // The first 0 means this can use a TestURLFetcherFactory in unit tests.
37 url_fetcher_
.reset(net::URLFetcher::Create(0,
41 url_fetcher_
->SetAutomaticallyRetryOn5xx(false);
42 url_fetcher_
->SetRequestContext(request_context_
.get());
44 // Can't safely use net::LOAD_DISABLE_CERT_REVOCATION_CHECKING here,
45 // since then the connection may be reused without checking the cert.
46 url_fetcher_
->SetLoadFlags(
47 net::LOAD_BYPASS_CACHE
|
48 net::LOAD_DO_NOT_PROMPT_FOR_LOGIN
|
49 net::LOAD_DO_NOT_SAVE_COOKIES
|
50 net::LOAD_DO_NOT_SEND_COOKIES
|
51 net::LOAD_DO_NOT_SEND_AUTH_DATA
);
52 url_fetcher_
->Start();
55 void CaptivePortalDetector::Cancel() {
57 detection_callback_
.Reset();
60 void CaptivePortalDetector::OnURLFetchComplete(const net::URLFetcher
* source
) {
61 // TODO(vadimt): Remove ScopedProfile below once crbug.com/422577 is fixed.
62 tracked_objects::ScopedProfile
tracking_profile(
63 FROM_HERE_WITH_EXPLICIT_FUNCTION(
64 "422577 CaptivePortalDetector::OnURLFetchComplete"));
66 DCHECK(CalledOnValidThread());
67 DCHECK(FetchingURL());
68 DCHECK_EQ(url_fetcher_
.get(), source
);
69 DCHECK(!detection_callback_
.is_null());
72 GetCaptivePortalResultFromResponse(url_fetcher_
.get(), &results
);
73 DetectionCallback callback
= detection_callback_
;
75 detection_callback_
.Reset();
76 callback
.Run(results
);
79 // Takes a net::URLFetcher that has finished trying to retrieve the test
80 // URL, and returns a CaptivePortalService::Result based on its result.
81 void CaptivePortalDetector::GetCaptivePortalResultFromResponse(
82 const net::URLFetcher
* url_fetcher
,
83 Results
* results
) const {
85 DCHECK(!url_fetcher
->GetStatus().is_io_pending());
87 results
->result
= captive_portal::RESULT_NO_RESPONSE
;
88 results
->response_code
= url_fetcher
->GetResponseCode();
89 results
->retry_after_delta
= base::TimeDelta();
90 results
->landing_url
= url_fetcher
->GetURL();
92 // If there's a network error of some sort when fetching a file via HTTP,
93 // there may be a networking problem, rather than a captive portal.
94 // TODO(mmenke): Consider special handling for redirects that end up at
95 // errors, especially SSL certificate errors.
96 if (url_fetcher
->GetStatus().status() != net::URLRequestStatus::SUCCESS
)
99 // In the case of 503 errors, look for the Retry-After header.
100 if (results
->response_code
== 503) {
101 net::HttpResponseHeaders
* headers
= url_fetcher
->GetResponseHeaders();
102 std::string retry_after_string
;
104 // If there's no Retry-After header, nothing else to do.
105 if (!headers
->EnumerateHeader(NULL
, "Retry-After", &retry_after_string
))
108 // Otherwise, try parsing it as an integer (seconds) or as an HTTP date.
110 base::Time full_date
;
111 if (base::StringToInt(retry_after_string
, &seconds
)) {
112 results
->retry_after_delta
= base::TimeDelta::FromSeconds(seconds
);
113 } else if (headers
->GetTimeValuedHeader("Retry-After", &full_date
)) {
114 base::Time now
= GetCurrentTime();
116 results
->retry_after_delta
= full_date
- now
;
121 // A 511 response (Network Authentication Required) means that the user needs
122 // to login to whatever server issued the response.
123 // See: http://tools.ietf.org/html/rfc6585
124 if (results
->response_code
== 511) {
125 results
->result
= captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL
;
129 // Other non-2xx/3xx HTTP responses may indicate server errors.
130 if (results
->response_code
>= 400 || results
->response_code
< 200)
133 // A 204 response code indicates there's no captive portal.
134 if (results
->response_code
== 204) {
135 results
->result
= captive_portal::RESULT_INTERNET_CONNECTED
;
139 // Otherwise, assume it's a captive portal.
140 results
->result
= captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL
;
143 base::Time
CaptivePortalDetector::GetCurrentTime() const {
144 if (time_for_testing_
.is_null())
145 return base::Time::Now();
147 return time_for_testing_
;
150 bool CaptivePortalDetector::FetchingURL() const {
151 return url_fetcher_
.get() != NULL
;
154 } // namespace captive_portal