blob: 3e673367ad8b076f5c05b00ce4d41217e250eb8c [file] [log] [blame]
// 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