Home | History | Annotate | Download | only in captive_portal
      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 
     18 CaptivePortalDetector::CaptivePortalDetector(
     19     const scoped_refptr<net::URLRequestContextGetter>& request_context)
     20     : request_context_(request_context) {
     21 }
     22 
     23 CaptivePortalDetector::~CaptivePortalDetector() {
     24 }
     25 
     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 
     54 void CaptivePortalDetector::Cancel() {
     55   url_fetcher_.reset();
     56   detection_callback_.Reset();
     57 }
     58 
     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.
     75 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 
    137 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 
    144 bool CaptivePortalDetector::FetchingURL() const {
    145   return url_fetcher_.get() != NULL;
    146 }
    147 
    148 }  // namespace captive_portal
    149