diff options
author | dilmah@chromium.org <dilmah@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-09 11:10:15 +0000 |
---|---|---|
committer | dilmah@chromium.org <dilmah@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-09 11:10:15 +0000 |
commit | 9b3c4fba2c771704c2ddafb431d87fd683c3d064 (patch) | |
tree | b2f20bfce5f739a4d19d824933b3e5047febba70 | |
parent | e28f486aaf3e7ce04ae2f02c342f9bf26dcba601 (diff) | |
download | chromium_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.cc | 95 | ||||
-rw-r--r-- | chrome/browser/chromeos/web_socket_proxy.h | 3 | ||||
-rw-r--r-- | chrome/browser/chromeos/web_socket_proxy_controller.cc | 66 |
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 - |