summaryrefslogtreecommitdiffstats
path: root/components/captive_portal/captive_portal_detector.cc
blob: ceeffe6b728c004d8e2fe50054de01d83bb80b7e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/captive_portal/captive_portal_detector.h"

#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "net/base/load_flags.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_util.h"
#include "net/url_request/url_request_status.h"

namespace captive_portal {

const char CaptivePortalDetector::kDefaultURL[] =
    "http://www.gstatic.com/generate_204";

CaptivePortalDetector::CaptivePortalDetector(
    const scoped_refptr<net::URLRequestContextGetter>& request_context)
    : request_context_(request_context) {
}

CaptivePortalDetector::~CaptivePortalDetector() {
}

void CaptivePortalDetector::DetectCaptivePortal(
    const GURL& url,
    const DetectionCallback& detection_callback) {
  DCHECK(CalledOnValidThread());
  DCHECK(!FetchingURL());
  DCHECK(detection_callback_.is_null());

  detection_callback_ = detection_callback;

  // The first 0 means this can use a TestURLFetcherFactory in unit tests.
  url_fetcher_ = net::URLFetcher::Create(0, url, net::URLFetcher::GET, this);
  url_fetcher_->SetAutomaticallyRetryOn5xx(false);
  url_fetcher_->SetRequestContext(request_context_.get());

  // Can't safely use net::LOAD_DISABLE_CERT_REVOCATION_CHECKING here,
  // since then the connection may be reused without checking the cert.
  url_fetcher_->SetLoadFlags(
      net::LOAD_BYPASS_CACHE |
      net::LOAD_DO_NOT_SAVE_COOKIES |
      net::LOAD_DO_NOT_SEND_COOKIES |
      net::LOAD_DO_NOT_SEND_AUTH_DATA);
  url_fetcher_->Start();
}

void CaptivePortalDetector::Cancel() {
  url_fetcher_.reset();
  detection_callback_.Reset();
}

void CaptivePortalDetector::OnURLFetchComplete(const net::URLFetcher* source) {
  DCHECK(CalledOnValidThread());
  DCHECK(FetchingURL());
  DCHECK_EQ(url_fetcher_.get(), source);
  DCHECK(!detection_callback_.is_null());

  Results results;
  GetCaptivePortalResultFromResponse(url_fetcher_.get(), &results);
  DetectionCallback callback = detection_callback_;
  url_fetcher_.reset();
  detection_callback_.Reset();
  callback.Run(results);
}

// Takes a net::URLFetcher that has finished trying to retrieve the test
// URL, and returns a CaptivePortalService::Result based on its result.
void CaptivePortalDetector::GetCaptivePortalResultFromResponse(
    const net::URLFetcher* url_fetcher,
    Results* results) const {
  DCHECK(results);
  DCHECK(!url_fetcher->GetStatus().is_io_pending());

  results->result = captive_portal::RESULT_NO_RESPONSE;
  results->response_code = url_fetcher->GetResponseCode();
  results->retry_after_delta = base::TimeDelta();
  results->landing_url = url_fetcher->GetURL();

  // If there's a network error of some sort when fetching a file via HTTP,
  // there may be a networking problem, rather than a captive portal.
  // TODO(mmenke):  Consider special handling for redirects that end up at
  //                errors, especially SSL certificate errors.
  if (url_fetcher->GetStatus().status() != net::URLRequestStatus::SUCCESS)
    return;

  // In the case of 503 errors, look for the Retry-After header.
  if (results->response_code == 503) {
    net::HttpResponseHeaders* headers = url_fetcher->GetResponseHeaders();
    std::string retry_after_string;

    // If there's no Retry-After header, nothing else to do.
    if (!headers->EnumerateHeader(NULL, "Retry-After", &retry_after_string))
      return;

    base::TimeDelta retry_after_delta;
    if (net::HttpUtil::ParseRetryAfterHeader(retry_after_string,
                                             GetCurrentTime(),
                                             &retry_after_delta)) {
      results->retry_after_delta = retry_after_delta;
    }

    return;
  }

  // A 511 response (Network Authentication Required) means that the user needs
  // to login to whatever server issued the response.
  // See:  http://tools.ietf.org/html/rfc6585
  if (results->response_code == 511) {
    results->result = captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL;
    return;
  }

  // Other non-2xx/3xx HTTP responses may indicate server errors.
  if (results->response_code >= 400 || results->response_code < 200)
    return;

  // A 204 response code indicates there's no captive portal.
  if (results->response_code == 204) {
    results->result = captive_portal::RESULT_INTERNET_CONNECTED;
    return;
  }

  // Otherwise, assume it's a captive portal.
  results->result = captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL;
}

base::Time CaptivePortalDetector::GetCurrentTime() const {
  if (time_for_testing_.is_null())
    return base::Time::Now();
  else
    return time_for_testing_;
}

bool CaptivePortalDetector::FetchingURL() const {
  return url_fetcher_.get() != NULL;
}

}  // namespace captive_portal