| // 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 "chrome/browser/chromeos/web_socket_proxy_controller.h" |
| |
| #include <algorithm> |
| |
| #include <netinet/in.h> |
| #include <sys/wait.h> |
| #include <unistd.h> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/command_line.h" |
| #include "base/lazy_instance.h" |
| #include "base/message_loop.h" |
| #include "base/string_tokenizer.h" |
| #include "base/threading/thread.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/chromeos/web_socket_proxy.h" |
| #include "chrome/common/chrome_notification_types.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/extensions/extension.h" |
| #include "chrome/common/url_constants.h" |
| #include "content/public/browser/notification_observer.h" |
| #include "content/public/browser/notification_registrar.h" |
| #include "content/public/browser/notification_service.h" |
| #include "content/public/common/url_constants.h" |
| #include "googleurl/src/gurl.h" |
| #include "net/base/network_change_notifier.h" |
| |
| namespace { |
| |
| class ProxyLifetime |
| : public net::NetworkChangeNotifier::ConnectionTypeObserver, |
| public content::NotificationObserver { |
| public: |
| ProxyLifetime() |
| : delay_ms_(1000), |
| port_(-1), |
| shutdown_requested_(false), |
| web_socket_proxy_thread_("Chrome_WebSocketproxyThread") { |
| DLOG(INFO) << "WebSocketProxyController initiation"; |
| base::Thread::Options options(MessageLoop::TYPE_IO, 0); |
| web_socket_proxy_thread_.StartWithOptions(options); |
| web_socket_proxy_thread_.message_loop()->PostTask( |
| FROM_HERE, |
| base::Bind(&ProxyLifetime::ProxyCallback, base::Unretained(this))); |
| net::NetworkChangeNotifier::AddConnectionTypeObserver(this); |
| registrar_.Add( |
| this, chrome::NOTIFICATION_WEB_SOCKET_PROXY_STARTED, |
| content::NotificationService::AllSources()); |
| } |
| |
| virtual ~ProxyLifetime() { |
| net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this); |
| } |
| |
| virtual void Observe(int type, const content::NotificationSource& source, |
| const content::NotificationDetails& details) OVERRIDE { |
| base::AutoLock alk(lock_); |
| port_ = *content::Details<int>(details).ptr(); |
| } |
| |
| int GetPort() { |
| base::AutoLock alk(lock_); |
| return port_; |
| } |
| |
| private: |
| // net::NetworkChangeNotifier::ConnectionTypeObserver implementation. |
| virtual void OnConnectionTypeChanged( |
| net::NetworkChangeNotifier::ConnectionType type) OVERRIDE { |
| DCHECK(chromeos::WebSocketProxyController::IsInitiated()); |
| base::AutoLock alk(lock_); |
| if (server_) |
| server_->OnNetworkChange(); |
| } |
| |
| void ProxyCallback() { |
| LOG(INFO) << "Attempt to run web socket proxy task"; |
| chromeos::WebSocketProxy* server = new chromeos::WebSocketProxy(); |
| { |
| base::AutoLock alk(lock_); |
| if (shutdown_requested_) |
| return; |
| delete server_; |
| server_ = server; |
| } |
| server->Run(); |
| { |
| base::AutoLock alk(lock_); |
| delete server; |
| server_ = NULL; |
| if (!shutdown_requested_) { |
| // Proxy terminated unexpectedly or failed to start (it can happen due |
| // to a network problem). Keep trying. |
| if (delay_ms_ < 100 * 1000) |
| (delay_ms_ *= 3) /= 2; |
| |
| MessageLoop::current()->PostDelayedTask( |
| FROM_HERE, |
| base::Bind(&ProxyLifetime::ProxyCallback, base::Unretained(this)), |
| base::TimeDelta::FromMilliseconds(delay_ms_)); |
| } |
| } |
| } |
| |
| // Delay in milliseconds between next attempt to run proxy. |
| int volatile delay_ms_; |
| |
| // Proxy listens for incoming websocket connections on this port. |
| int volatile port_; |
| |
| chromeos::WebSocketProxy* volatile server_; |
| volatile bool shutdown_requested_; |
| base::Lock lock_; |
| content::NotificationRegistrar registrar_; |
| friend class chromeos::WebSocketProxyController; |
| base::Thread web_socket_proxy_thread_; |
| }; |
| |
| base::LazyInstance<ProxyLifetime> g_proxy_lifetime = LAZY_INSTANCE_INITIALIZER; |
| |
| } // namespace |
| |
| namespace chromeos { |
| |
| // static |
| void WebSocketProxyController::Initiate() { |
| g_proxy_lifetime.Get(); |
| } |
| |
| // static |
| bool WebSocketProxyController::IsInitiated() { |
| return !(g_proxy_lifetime == NULL); |
| } |
| |
| // static |
| int WebSocketProxyController::GetPort() { |
| int port = g_proxy_lifetime.Get().GetPort(); |
| DCHECK(IsInitiated()); |
| return port; |
| } |
| |
| // static |
| void WebSocketProxyController::Shutdown() { |
| if (!IsInitiated()) |
| return; |
| |
| DLOG(INFO) << "WebSocketProxyController shutdown"; |
| { |
| base::AutoLock alk(g_proxy_lifetime.Get().lock_); |
| g_proxy_lifetime.Get().shutdown_requested_ = true; |
| if (g_proxy_lifetime.Get().server_) |
| g_proxy_lifetime.Get().server_->Shutdown(); |
| } |
| g_proxy_lifetime.Get().web_socket_proxy_thread_.Stop(); |
| } |
| |
| } // namespace chromeos |