1 // Copyright 2015 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 "chromecast/net/connectivity_checker_impl.h"
7 #include "base/command_line.h"
8 #include "base/logging.h"
9 #include "base/message_loop/message_loop.h"
10 #include "chromecast/net/net_switches.h"
11 #include "net/base/request_priority.h"
12 #include "net/http/http_response_headers.h"
13 #include "net/http/http_response_info.h"
14 #include "net/http/http_status_code.h"
15 #include "net/proxy/proxy_config.h"
16 #include "net/proxy/proxy_config_service_fixed.h"
17 #include "net/socket/ssl_client_socket.h"
18 #include "net/url_request/url_request_context.h"
19 #include "net/url_request/url_request_context_builder.h"
21 namespace chromecast
{
25 // How often connectivity checks are performed in seconds.
26 const unsigned int kConnectivityPeriodSeconds
= 1;
28 // Number of consecutive connectivity check errors before status is changed
30 const unsigned int kNumErrorsToNotifyOffline
= 3;
32 // Request timeout value in seconds.
33 const unsigned int kRequestTimeoutInSeconds
= 3;
35 // Default url for connectivity checking.
36 const char kDefaultConnectivityCheckUrl
[] =
37 "https://clients3.google.com/generate_204";
41 ConnectivityCheckerImpl::ConnectivityCheckerImpl(
42 const scoped_refptr
<base::SingleThreadTaskRunner
>& task_runner
)
43 : ConnectivityChecker(),
44 task_runner_(task_runner
),
47 DCHECK(task_runner_
.get());
48 task_runner
->PostTask(FROM_HERE
,
49 base::Bind(&ConnectivityCheckerImpl::Initialize
, this));
52 void ConnectivityCheckerImpl::Initialize() {
53 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
54 base::CommandLine::StringType check_url_str
=
55 command_line
->GetSwitchValueNative(switches::kConnectivityCheckUrl
);
56 connectivity_check_url_
.reset(new GURL(
57 check_url_str
.empty() ? kDefaultConnectivityCheckUrl
: check_url_str
));
59 net::URLRequestContextBuilder builder
;
60 builder
.set_proxy_config_service(make_scoped_ptr(
61 new net::ProxyConfigServiceFixed(net::ProxyConfig::CreateDirect())));
62 builder
.DisableHttpCache();
63 url_request_context_
= builder
.Build().Pass();
65 net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
66 task_runner_
->PostTask(FROM_HERE
,
67 base::Bind(&ConnectivityCheckerImpl::Check
, this));
70 ConnectivityCheckerImpl::~ConnectivityCheckerImpl() {
71 DCHECK(task_runner_
.get());
72 net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
73 task_runner_
->DeleteSoon(FROM_HERE
, url_request_
.release());
74 task_runner_
->DeleteSoon(FROM_HERE
, url_request_context_
.release());
77 bool ConnectivityCheckerImpl::Connected() const {
81 void ConnectivityCheckerImpl::SetConnected(bool connected
) {
82 if (connected_
== connected
)
85 connected_
= connected
;
87 LOG(INFO
) << "Global connection is: " << (connected
? "Up" : "Down");
90 void ConnectivityCheckerImpl::Check() {
91 if (!task_runner_
->BelongsToCurrentThread()) {
92 task_runner_
->PostTask(FROM_HERE
,
93 base::Bind(&ConnectivityCheckerImpl::Check
, this));
96 DCHECK(url_request_context_
.get());
98 // Don't check connectivity if network is offline, because Internet could be
99 // accessible via netifs ignored.
100 if (net::NetworkChangeNotifier::IsOffline())
103 // If url_request_ is non-null, there is already a check going on. Don't
105 if (url_request_
.get())
108 VLOG(1) << "Connectivity check: url=" << *connectivity_check_url_
;
109 url_request_
= url_request_context_
->CreateRequest(
110 *connectivity_check_url_
, net::MAXIMUM_PRIORITY
, this);
111 url_request_
->set_method("HEAD");
112 url_request_
->Start();
114 timeout_
.Reset(base::Bind(&ConnectivityCheckerImpl::OnUrlRequestTimeout
,
116 // Exponential backoff for timeout in 3, 6 and 12 sec.
117 const int timeout
= kRequestTimeoutInSeconds
118 << (check_errors_
> 2 ? 2 : check_errors_
);
119 base::MessageLoop::current()->PostDelayedTask(
122 base::TimeDelta::FromSeconds(timeout
));
125 void ConnectivityCheckerImpl::OnNetworkChanged(
126 net::NetworkChangeNotifier::ConnectionType type
) {
127 VLOG(2) << "OnNetworkChanged " << type
;
130 if (type
== net::NetworkChangeNotifier::CONNECTION_NONE
) {
138 void ConnectivityCheckerImpl::OnResponseStarted(net::URLRequest
* request
) {
140 int http_response_code
=
141 (request
->status().is_success() &&
142 request
->response_info().headers
.get() != NULL
)
143 ? request
->response_info().headers
->response_code()
144 : net::HTTP_BAD_REQUEST
;
147 url_request_
.reset(NULL
); // URLRequest::Cancel() is called in destructor.
149 if (http_response_code
< 400) {
150 VLOG(1) << "Connectivity check succeeded";
155 VLOG(1) << "Connectivity check failed: " << http_response_code
;
159 void ConnectivityCheckerImpl::OnReadCompleted(net::URLRequest
* request
,
164 void ConnectivityCheckerImpl::OnSSLCertificateError(
165 net::URLRequest
* request
,
166 const net::SSLInfo
& ssl_info
,
168 LOG(ERROR
) << "OnSSLCertificateError: cert_status=" << ssl_info
.cert_status
;
170 net::SSLClientSocket::ClearSessionCache();
174 void ConnectivityCheckerImpl::OnUrlRequestError() {
176 if (check_errors_
> kNumErrorsToNotifyOffline
) {
177 check_errors_
= kNumErrorsToNotifyOffline
;
180 url_request_
.reset(NULL
);
182 task_runner_
->PostDelayedTask(
183 FROM_HERE
, base::Bind(&ConnectivityCheckerImpl::Check
, this),
184 base::TimeDelta::FromSeconds(kConnectivityPeriodSeconds
));
187 void ConnectivityCheckerImpl::OnUrlRequestTimeout() {
188 LOG(ERROR
) << "time out";
192 void ConnectivityCheckerImpl::Cancel() {
193 if (!url_request_
.get())
195 VLOG(2) << "Cancel connectivity check in progress";
197 url_request_
.reset(NULL
); // URLRequest::Cancel() is called in destructor.
200 } // namespace chromecast