Index: chromecast/net/connectivity_checker.cc |
diff --git a/chromecast/net/connectivity_checker.cc b/chromecast/net/connectivity_checker.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e64ac47d2f7c7e7fb73d0ea2a25cc06a22cc552b |
--- /dev/null |
+++ b/chromecast/net/connectivity_checker.cc |
@@ -0,0 +1,173 @@ |
+// Copyright 2015 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 "chromecast/net/connectivity_checker.h" |
+ |
+#include "base/command_line.h" |
+#include "base/logging.h" |
+#include "base/message_loop/message_loop.h" |
+#include "chromecast/net/net_switches.h" |
+#include "net/base/request_priority.h" |
+#include "net/http/http_response_headers.h" |
+#include "net/http/http_response_info.h" |
+#include "net/http/http_status_code.h" |
+#include "net/proxy/proxy_config.h" |
+#include "net/proxy/proxy_config_service_fixed.h" |
+#include "net/url_request/url_request_context.h" |
+#include "net/url_request/url_request_context_builder.h" |
+ |
+namespace chromecast { |
+ |
+namespace { |
+ |
+// How often connectivity checks are performed in seconds |
+const unsigned int kConnectivityPeriodSeconds = 1; |
+ |
+// Number of consecutive bad responses received before connectivity status is |
+// changed to offline |
+const unsigned int kNumBadResponses = 3; |
+ |
+// Default url for connectivity checking. |
+const char kDefaultConnectivityCheckUrl[] = |
+ "https://clients3.google.com/generate_204"; |
+ |
+} // namespace |
+ |
+ConnectivityChecker::ConnectivityChecker( |
+ const scoped_refptr<base::MessageLoopProxy>& loop_proxy) |
+ : connectivity_observer_list_( |
+ new ObserverListThreadSafe<ConnectivityObserver>()), |
+ loop_proxy_(loop_proxy), |
+ connected_(false), |
+ bad_responses_(0) { |
+ DCHECK(loop_proxy_.get()); |
+ loop_proxy->PostTask(FROM_HERE, |
+ base::Bind(&ConnectivityChecker::Initialize, this)); |
+} |
+ |
+void ConnectivityChecker::Initialize() { |
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
+ base::CommandLine::StringType check_url_str = |
+ command_line->GetSwitchValueNative(switches::kConnectivityCheckUrl); |
+ connectivity_check_url_.reset(new GURL( |
+ check_url_str.empty() ? kDefaultConnectivityCheckUrl : check_url_str)); |
+ |
+ net::URLRequestContextBuilder builder; |
+ builder.set_proxy_config_service( |
+ new net::ProxyConfigServiceFixed(net::ProxyConfig::CreateDirect())); |
+ builder.DisableHttpCache(); |
+ url_request_context_.reset(builder.Build()); |
+ |
+ net::NetworkChangeNotifier::AddConnectionTypeObserver(this); |
+ if (!net::NetworkChangeNotifier::IsOffline()) { |
+ loop_proxy_->PostTask(FROM_HERE, |
+ base::Bind(&ConnectivityChecker::Check, this)); |
+ } |
+} |
+ |
+ConnectivityChecker::~ConnectivityChecker() { |
+ DCHECK(loop_proxy_.get()); |
+ loop_proxy_->DeleteSoon(FROM_HERE, url_request_context_.release()); |
+} |
+ |
+void ConnectivityChecker::AddConnectivityObserver( |
+ ConnectivityObserver* observer) { |
+ connectivity_observer_list_->AddObserver(observer); |
+} |
+ |
+void ConnectivityChecker::RemoveConnectivityObserver( |
+ ConnectivityObserver* observer) { |
+ connectivity_observer_list_->RemoveObserver(observer); |
+} |
+ |
+bool ConnectivityChecker::Connected() const { |
+ return connected_; |
+} |
+ |
+void ConnectivityChecker::SetConnectivity(bool connected) { |
+ if (connected_ == connected) |
+ return; |
+ |
+ connected_ = connected; |
+ connectivity_observer_list_->Notify( |
+ FROM_HERE, &ConnectivityObserver::OnConnectivityChanged, connected); |
+ LOG(INFO) << "Global connection is: " << (connected ? "Up" : "Down"); |
+} |
+ |
+void ConnectivityChecker::Check() { |
+ if (!loop_proxy_->BelongsToCurrentThread()) { |
+ loop_proxy_->PostTask(FROM_HERE, |
+ base::Bind(&ConnectivityChecker::Check, this)); |
+ return; |
+ } |
+ DCHECK(url_request_context_.get()); |
+ |
+ // If url_request_ is non-null, there is already a check going on. Don't |
+ // start another. |
+ if (url_request_.get()) |
+ return; |
+ |
+ VLOG(2) << "Connectivity check: url=" << *connectivity_check_url_; |
+ url_request_ = url_request_context_->CreateRequest( |
+ *connectivity_check_url_, net::MAXIMUM_PRIORITY, this, NULL); |
+ url_request_->set_method("HEAD"); |
+ url_request_->Start(); |
+} |
+ |
+void ConnectivityChecker::OnConnectionTypeChanged( |
+ net::NetworkChangeNotifier::ConnectionType type) { |
+ Cancel(); |
+ |
+ // If there is no connection, set connectivity to false. Otherwise retest |
+ // connectivity |
+ if (type == net::NetworkChangeNotifier::CONNECTION_NONE) { |
+ SetConnectivity(false); |
+ } else { |
+ Check(); |
+ } |
+} |
+ |
+void ConnectivityChecker::OnResponseStarted(net::URLRequest* request) { |
+ int http_response_code = |
+ (request->status().is_success() && |
+ request->response_info().headers.get() != NULL) |
+ ? request->response_info().headers->response_code() |
+ : net::HTTP_BAD_REQUEST; |
+ |
+ // Clears resources. |
+ url_request_.reset(NULL); // URLRequest::Cancel() is called in destructor. |
+ |
+ if (http_response_code < 400) { |
+ bad_responses_ = 0; |
+ SetConnectivity(true); |
+ return; |
+ } |
+ |
+ ++bad_responses_; |
+ if (bad_responses_ > kNumBadResponses) { |
+ bad_responses_ = kNumBadResponses; |
+ SetConnectivity(false); |
+ } |
+ |
+ // Check again |
+ if (!net::NetworkChangeNotifier::IsOffline()) { |
+ loop_proxy_->PostDelayedTask( |
+ FROM_HERE, base::Bind(&ConnectivityChecker::Check, this), |
+ base::TimeDelta::FromSeconds(kConnectivityPeriodSeconds)); |
+ } |
+} |
+ |
+void ConnectivityChecker::OnReadCompleted(net::URLRequest* request, |
+ int bytes_read) { |
+ NOTREACHED(); |
+} |
+ |
+void ConnectivityChecker::Cancel() { |
+ if (url_request_.get()) { |
+ VLOG(2) << "Cancel connectivity check in progress"; |
+ url_request_.reset(NULL); // URLRequest::Cancel() is called in destructor. |
+ } |
+} |
+ |
+} // namespace chromecast |