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