summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordilmah@chromium.org <dilmah@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-09 11:10:15 +0000
committerdilmah@chromium.org <dilmah@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-09 11:10:15 +0000
commit9b3c4fba2c771704c2ddafb431d87fd683c3d064 (patch)
treeb2f20bfce5f739a4d19d824933b3e5047febba70
parente28f486aaf3e7ce04ae2f02c342f9bf26dcba601 (diff)
downloadchromium_src-9b3c4fba2c771704c2ddafb431d87fd683c3d064.zip
chromium_src-9b3c4fba2c771704c2ddafb431d87fd683c3d064.tar.gz
chromium_src-9b3c4fba2c771704c2ddafb431d87fd683c3d064.tar.bz2
Handle network change in WS-to-TCP proxy.
BUG=chromium-os:20111 TEST=Manual Review URL: http://codereview.chromium.org/7838024 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@100386 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/chromeos/web_socket_proxy.cc95
-rw-r--r--chrome/browser/chromeos/web_socket_proxy.h3
-rw-r--r--chrome/browser/chromeos/web_socket_proxy_controller.cc66
3 files changed, 110 insertions, 54 deletions
diff --git a/chrome/browser/chromeos/web_socket_proxy.cc b/chrome/browser/chromeos/web_socket_proxy.cc
index d9a6a9a..e4932e3 100644
--- a/chrome/browser/chromeos/web_socket_proxy.cc
+++ b/chrome/browser/chromeos/web_socket_proxy.cc
@@ -68,6 +68,10 @@ enum WebSocketFrameOpcode {
WS_OPCODE_CLOSE = 8
};
+// Fixed-size (one-byte) messages communicated via control pipe.
+const char kControlMessageShutdown[] = { '.' };
+const char kControlMessageNetworkChange[] = { ':' };
+
// Returns true on success.
bool SetNonBlock(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
@@ -212,6 +216,8 @@ class Serv {
// Terminates running server (should be called on a different thread).
void Shutdown();
+ // Called on network change. Reinitializes DNS resolution service.
+ void OnNetworkChange();
void ZapConn(Conn*);
void MarkConnImportance(Conn*, bool important);
@@ -221,7 +227,7 @@ class Serv {
void CloseAll();
static void OnConnect(int listening_sock, short event, void*);
- static void OnShutdownRequest(int fd, short event, void*);
+ static void OnControlRequest(int fd, short event, void*);
struct event_base* evbase() { return evbase_; }
@@ -239,8 +245,9 @@ class Serv {
// Socket to listen incoming websocket connections.
int listening_sock_;
- // Event on this descriptor triggers server shutdown.
- int shutdown_descriptor_[2];
+ // Used to communicate control requests: either shutdown request or network
+ // change notification.
+ int control_descriptor_[2];
// Flag whether shutdown has been requested.
bool shutdown_requested_;
@@ -255,7 +262,7 @@ class Serv {
RevMap rev_map_;
scoped_ptr<struct event> connection_event_;
- scoped_ptr<struct event> shutdown_event_;
+ scoped_ptr<struct event> control_event_;
DISALLOW_COPY_AND_ASSIGN(Serv);
};
@@ -436,8 +443,8 @@ Serv::Serv(
listening_sock_(-1),
shutdown_requested_(false) {
std::sort(allowed_origins_.begin(), allowed_origins_.end());
- shutdown_descriptor_[0] = -1;
- shutdown_descriptor_[1] = -1;
+ control_descriptor_[0] = -1;
+ control_descriptor_[1] = -1;
}
Serv::~Serv() {
@@ -454,8 +461,8 @@ void Serv::Run() {
return;
}
- if (pipe(shutdown_descriptor_)) {
- LOG(ERROR) << "WebSocketProxy: Failed to create shutdown pipe";
+ if (pipe(control_descriptor_)) {
+ LOG(ERROR) << "WebSocketProxy: Failed to create control pipe";
return;
}
@@ -490,19 +497,17 @@ void Serv::Run() {
return;
}
- shutdown_event_.reset(new struct event);
- event_set(shutdown_event_.get(), shutdown_descriptor_[0], EV_READ,
- &OnShutdownRequest, this);
- event_base_set(evbase_, shutdown_event_.get());
- if (event_add(shutdown_event_.get(), NULL)) {
- LOG(ERROR) << "WebSocketProxy: Failed to add shutdown event";
+ control_event_.reset(new struct event);
+ event_set(control_event_.get(), control_descriptor_[0], EV_READ | EV_PERSIST,
+ &OnControlRequest, this);
+ event_base_set(evbase_, control_event_.get());
+ if (event_add(control_event_.get(), NULL)) {
+ LOG(ERROR) << "WebSocketProxy: Failed to add control event";
return;
}
- if (evdns_init()) {
- LOG(ERROR) << "WebSocketProxy: Failed to initialize evDNS";
- return;
- }
+ if (evdns_init())
+ LOG(WARNING) << "WebSocketProxy: Failed to initialize evDNS";
if (!IgnoreSigPipe()) {
LOG(ERROR) << "WebSocketProxy: Failed to ignore SIGPIPE";
return;
@@ -522,8 +527,13 @@ void Serv::Run() {
}
void Serv::Shutdown() {
- if (1 != write(shutdown_descriptor_[1], ".", 1))
- NOTREACHED();
+ int r ALLOW_UNUSED =
+ write(control_descriptor_[1], kControlMessageShutdown, 1);
+}
+
+void Serv::OnNetworkChange() {
+ int r ALLOW_UNUSED =
+ write(control_descriptor_[1], kControlMessageNetworkChange, 1);
}
void Serv::CloseAll() {
@@ -534,14 +544,14 @@ void Serv::CloseAll() {
close(listening_sock_);
}
for (int i = 0; i < 2; ++i) {
- if (shutdown_descriptor_[i] >= 0) {
- shutdown_descriptor_[i] = -1;
- close(shutdown_descriptor_[i]);
+ if (control_descriptor_[i] >= 0) {
+ control_descriptor_[i] = -1;
+ close(control_descriptor_[i]);
}
}
- if (shutdown_event_.get()) {
- event_del(shutdown_event_.get());
- shutdown_event_.reset();
+ if (control_event_.get()) {
+ event_del(control_event_.get());
+ control_event_.reset();
}
if (connection_event_.get()) {
event_del(connection_event_.get());
@@ -649,10 +659,19 @@ void Serv::OnConnect(int listening_sock, short event, void* ctx) {
}
// static
-void Serv::OnShutdownRequest(int fd, short event, void* ctx) {
+void Serv::OnControlRequest(int fd, short event, void* ctx) {
Serv* self = static_cast<Serv*>(ctx);
- self->shutdown_requested_ = true;
- event_base_loopbreak(self->evbase_);
+
+ char c;
+ if (1 == read(fd, &c, 1) && c == *kControlMessageNetworkChange) {
+ // OnNetworkChange request.
+ evdns_clear_nameservers_and_suspend();
+ evdns_init();
+ evdns_resume();
+ } else if (c == *kControlMessageShutdown) {
+ self->shutdown_requested_ = true;
+ event_base_loopbreak(self->evbase_);
+ }
}
Conn::Conn(Serv* master)
@@ -1041,6 +1060,13 @@ void Conn::OnPrimchanRead(struct bufferevent* bev, EventKey evkey) {
switch (cs->ConsumeDestframe(EVBUFFER_INPUT(bev))) {
case STATUS_OK: {
{
+ // Unfortunately libevent as of 1.4 does not look into /etc/hosts.
+ // There seems to be no easy API to perform only "local" part of
+ // getaddrinfo resolution. Hence this hack for "localhost".
+ if (cs->destname_ == "localhost")
+ cs->destname_ = "127.0.0.1";
+ }
+ {
struct sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_port = htons(cs->destport_);
@@ -1075,7 +1101,12 @@ void Conn::OnPrimchanRead(struct bufferevent* bev, EventKey evkey) {
}
}
}
- // Try to asynchronously perform DNS resolution.
+ // Asynchronous DNS resolution.
+ if (evdns_count_nameservers() < 1) {
+ evdns_clear_nameservers_and_suspend();
+ evdns_init();
+ evdns_resume();
+ }
evdns_resolve_ipv4(cs->destname_.c_str(), 0,
&OnDestResolutionIPv4, evkey);
evdns_resolve_ipv6(cs->destname_.c_str(), 0,
@@ -1361,5 +1392,9 @@ void WebSocketProxy::Shutdown() {
static_cast<Serv*>(impl_)->Shutdown();
}
+void WebSocketProxy::OnNetworkChange() {
+ static_cast<Serv*>(impl_)->OnNetworkChange();
+}
+
} // namespace chromeos
diff --git a/chrome/browser/chromeos/web_socket_proxy.h b/chrome/browser/chromeos/web_socket_proxy.h
index 09e783a..3d5cf57 100644
--- a/chrome/browser/chromeos/web_socket_proxy.h
+++ b/chrome/browser/chromeos/web_socket_proxy.h
@@ -36,6 +36,9 @@ class WebSocketProxy {
// Terminates running server (should be called on a different thread).
void Shutdown();
+ // Call this on network change.
+ void OnNetworkChange();
+
private:
void* impl_;
diff --git a/chrome/browser/chromeos/web_socket_proxy_controller.cc b/chrome/browser/chromeos/web_socket_proxy_controller.cc
index 2e202c7..10f9789 100644
--- a/chrome/browser/chromeos/web_socket_proxy_controller.cc
+++ b/chrome/browser/chromeos/web_socket_proxy_controller.cc
@@ -17,9 +17,13 @@
#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 "content/browser/browser_thread.h"
+#include "content/common/notification_observer.h"
+#include "content/common/notification_registrar.h"
+#include "content/common/notification_service.h"
#include "content/common/url_constants.h"
#include "googleurl/src/gurl.h"
@@ -98,20 +102,35 @@ class ProxyTask : public Task {
virtual void Run() OVERRIDE;
};
-struct ProxyLifetime {
- ProxyLifetime() : delay_ms(1000), shutdown_requested(false) {
+class ProxyLifetime : public NotificationObserver {
+ public:
+ ProxyLifetime() : delay_ms_(1000), shutdown_requested_(false) {
BrowserThread::PostTask(
BrowserThread::WEB_SOCKET_PROXY, FROM_HERE, new ProxyTask());
+ registrar_.Add(this, chrome::NOTIFICATION_NETWORK_STATE_CHANGED,
+ NotificationService::AllSources());
}
- // Delay between next attempt to run proxy.
- int delay_ms;
-
- chromeos::WebSocketProxy* volatile server;
-
- volatile bool shutdown_requested;
+ private:
+ virtual void Observe(int type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK_EQ(type, chrome::NOTIFICATION_NETWORK_STATE_CHANGED);
+ DCHECK(chromeos::WebSocketProxyController::IsInitiated());
+ base::AutoLock alk(lock_);
+ if (server_)
+ server_->OnNetworkChange();
+ }
- base::Lock lock;
+ // Delay between next attempt to run proxy.
+ int delay_ms_;
+
+ chromeos::WebSocketProxy* volatile server_;
+ volatile bool shutdown_requested_;
+ base::Lock lock_;
+ NotificationRegistrar registrar_;
+ friend class ProxyTask;
+ friend class chromeos::WebSocketProxyController;
};
base::LazyInstance<ProxyLifetime> g_proxy_lifetime(base::LINKER_INITIALIZED);
@@ -130,25 +149,25 @@ void ProxyTask::Run() {
g_validator.Get().allowed_origins(),
reinterpret_cast<sockaddr*>(&sa), sizeof(sa));
{
- base::AutoLock alk(g_proxy_lifetime.Get().lock);
- if (g_proxy_lifetime.Get().shutdown_requested)
+ base::AutoLock alk(g_proxy_lifetime.Get().lock_);
+ if (g_proxy_lifetime.Get().shutdown_requested_)
return;
- delete g_proxy_lifetime.Get().server;
- g_proxy_lifetime.Get().server = server;
+ delete g_proxy_lifetime.Get().server_;
+ g_proxy_lifetime.Get().server_ = server;
}
server->Run();
{
- base::AutoLock alk(g_proxy_lifetime.Get().lock);
+ base::AutoLock alk(g_proxy_lifetime.Get().lock_);
delete server;
- g_proxy_lifetime.Get().server = NULL;
- if (!g_proxy_lifetime.Get().shutdown_requested) {
+ g_proxy_lifetime.Get().server_ = NULL;
+ if (!g_proxy_lifetime.Get().shutdown_requested_) {
// Proxy terminated unexpectedly or failed to start (it can happen due to
// a network problem). Keep trying.
- if (g_proxy_lifetime.Get().delay_ms < 100 * 1000)
- (g_proxy_lifetime.Get().delay_ms *= 3) /= 2;
+ if (g_proxy_lifetime.Get().delay_ms_ < 100 * 1000)
+ (g_proxy_lifetime.Get().delay_ms_ *= 3) /= 2;
BrowserThread::PostDelayedTask(
BrowserThread::WEB_SOCKET_PROXY, FROM_HERE, new ProxyTask(),
- g_proxy_lifetime.Get().delay_ms);
+ g_proxy_lifetime.Get().delay_ms_);
}
}
}
@@ -178,10 +197,10 @@ bool WebSocketProxyController::IsInitiated() {
void WebSocketProxyController::Shutdown() {
if (IsInitiated()) {
LOG(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();
+ 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();
}
}
@@ -196,4 +215,3 @@ bool WebSocketProxyController::CheckCredentials(
}
} // namespace chromeos
-