diff options
author | rmsousa@chromium.org <rmsousa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-30 05:10:43 +0000 |
---|---|---|
committer | rmsousa@chromium.org <rmsousa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-30 05:10:43 +0000 |
commit | 49aa8fa463615a4d4ad345b492fab72aba0f8a27 (patch) | |
tree | 53fb835d6b2e51fcc05bd6529352d09492f15260 /remoting | |
parent | dc1f8546772aaaf0460afedd894c0c1c270b8890 (diff) | |
download | chromium_src-49aa8fa463615a4d4ad345b492fab72aba0f8a27.zip chromium_src-49aa8fa463615a4d4ad345b492fab72aba0f8a27.tar.gz chromium_src-49aa8fa463615a4d4ad345b492fab72aba0f8a27.tar.bz2 |
Implement a listener for host deletion notifications.
The directory server notifies the host (via the bot) when an entry is deleted (e.g. by clicking X on the webapp), and the host exits immediately (rather than up to 5 minutes later, when its next heartbeat failed).
BUG=163572
Review URL: https://chromiumcodereview.appspot.com/12045085
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@179533 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r-- | remoting/host/host_change_notification_listener.cc | 80 | ||||
-rw-r--r-- | remoting/host/host_change_notification_listener.h | 59 | ||||
-rw-r--r-- | remoting/host/host_change_notification_listener_unittest.cc | 147 | ||||
-rw-r--r-- | remoting/host/remoting_me2me_host.cc | 18 | ||||
-rw-r--r-- | remoting/remoting.gyp | 3 |
5 files changed, 306 insertions, 1 deletions
diff --git a/remoting/host/host_change_notification_listener.cc b/remoting/host/host_change_notification_listener.cc new file mode 100644 index 0000000..995928b --- /dev/null +++ b/remoting/host/host_change_notification_listener.cc @@ -0,0 +1,80 @@ +// Copyright (c) 2013 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 "remoting/host/host_change_notification_listener.h" + +#include "base/bind.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" +#include "remoting/base/constants.h" +#include "remoting/jingle_glue/signal_strategy.h" +#include "remoting/protocol/jingle_messages.h" +#include "third_party/libjingle/source/talk/xmllite/xmlelement.h" +#include "third_party/libjingle/source/talk/xmpp/constants.h" + +using buzz::QName; +using buzz::XmlElement; + +namespace remoting { + +HostChangeNotificationListener::HostChangeNotificationListener( + Listener* listener, + const std::string& host_id, + SignalStrategy* signal_strategy) + : listener_(listener), + host_id_(host_id), + signal_strategy_(signal_strategy), + ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { + DCHECK(signal_strategy_); + + signal_strategy_->AddListener(this); +} + +HostChangeNotificationListener::~HostChangeNotificationListener() { + signal_strategy_->RemoveListener(this); +} + +void HostChangeNotificationListener::OnSignalStrategyStateChange( + SignalStrategy::State state) { +} + +bool HostChangeNotificationListener::OnSignalStrategyIncomingStanza( + const buzz::XmlElement* stanza) { + if (stanza->Name() != buzz::QN_IQ || stanza->Attr(buzz::QN_TYPE) != "set") + return false; + + const XmlElement* host_changed_element = + stanza->FirstNamed(QName(kChromotingXmlNamespace, "host-changed")); + if (!host_changed_element) + return false; + + const std::string& host_id = + host_changed_element->Attr(QName(kChromotingXmlNamespace, "hostid")); + const std::string& from = stanza->Attr(buzz::QN_FROM); + const std::string& to = stanza->Attr(buzz::QN_TO); + if (host_id == host_id_ && from == kChromotingBotJid && + to == signal_strategy_->GetLocalJid()) { + const std::string& operation = + host_changed_element->Attr(QName(kChromotingXmlNamespace, "operation")); + if (operation == "delete") { + // OnHostDeleted() may want delete |signal_strategy_|, but SignalStrategy + // objects cannot be deleted from a Listener callback, so OnHostDeleted() + // has to be invoked later. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&HostChangeNotificationListener::OnHostDeleted, + weak_factory_.GetWeakPtr())); + } + } else { + LOG(ERROR) << "Invalid host-changed message received: " << stanza->Str(); + } + return true; +} + +void HostChangeNotificationListener::OnHostDeleted() { + listener_->OnHostDeleted(); +} + +} // namespace remoting diff --git a/remoting/host/host_change_notification_listener.h b/remoting/host/host_change_notification_listener.h new file mode 100644 index 0000000..19ee106 --- /dev/null +++ b/remoting/host/host_change_notification_listener.h @@ -0,0 +1,59 @@ +// Copyright (c) 2013 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. + +#ifndef REMOTING_HOST_HOST_CHANGE_NOTIFICATION_LISTENER_H +#define REMOTING_HOST_HOST_CHANGE_NOTIFICATION_LISTENER_H + +#include <string> + +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "remoting/jingle_glue/signal_strategy.h" + +namespace buzz { +class XmlElement; +} // namespace buzz + +namespace remoting { + +// HostChangeNotificationListener listens for messages from the remoting bot +// indicating that its host entry has been changed in the directory. +// If a message is received indicating that the host was deleted, it uses the +// OnHostDeleted callback to shut down the host. +class HostChangeNotificationListener : public SignalStrategy::Listener { + public: + class Listener { + protected: + virtual ~Listener() {} + // Invoked when a notification that the host was deleted is received. + public: + virtual void OnHostDeleted() = 0; + }; + + // Both listener and signal_strategy are expected to outlive this object. + HostChangeNotificationListener(Listener* listener, + const std::string& host_id, + SignalStrategy* signal_strategy); + virtual ~HostChangeNotificationListener(); + + // SignalStrategy::Listener interface. + virtual void OnSignalStrategyStateChange( + SignalStrategy::State state) OVERRIDE; + virtual bool OnSignalStrategyIncomingStanza( + const buzz::XmlElement* stanza) OVERRIDE; + + private: + void OnHostDeleted(); + + Listener* listener_; + std::string host_id_; + SignalStrategy* signal_strategy_; + base::WeakPtrFactory<HostChangeNotificationListener> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(HostChangeNotificationListener); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_HOST_CHANGE_NOTIFICATION_LISTENER_H diff --git a/remoting/host/host_change_notification_listener_unittest.cc b/remoting/host/host_change_notification_listener_unittest.cc new file mode 100644 index 0000000..a0f124f --- /dev/null +++ b/remoting/host/host_change_notification_listener_unittest.cc @@ -0,0 +1,147 @@ +// Copyright (c) 2013 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 "remoting/host/host_change_notification_listener.h" + +#include <set> + +#include "base/bind.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop.h" +#include "base/string_number_conversions.h" +#include "remoting/base/constants.h" +#include "remoting/jingle_glue/mock_objects.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/libjingle/source/talk/xmllite/xmlelement.h" +#include "third_party/libjingle/source/talk/xmpp/constants.h" + +using buzz::QName; +using buzz::XmlElement; + +using testing::NotNull; +using testing::Return; + +namespace remoting { + +namespace { +const char kHostId[] = "0"; +const char kTestJid[] = "user@gmail.com/chromoting123"; +} // namespace + +ACTION_P(AddListener, list) { + list->insert(arg0); +} + +ACTION_P(RemoveListener, list) { + EXPECT_TRUE(list->find(arg0) != list->end()); + list->erase(arg0); +} + + +class HostChangeNotificationListenerTest : public testing::Test { + protected: + class MockListener : public HostChangeNotificationListener::Listener { + public: + MOCK_METHOD0(OnHostDeleted, void()); + }; + + virtual void SetUp() OVERRIDE { + EXPECT_CALL(signal_strategy_, AddListener(NotNull())) + .WillRepeatedly(AddListener(&signal_strategy_listeners_)); + EXPECT_CALL(signal_strategy_, RemoveListener(NotNull())) + .WillRepeatedly(RemoveListener(&signal_strategy_listeners_)); + EXPECT_CALL(signal_strategy_, GetLocalJid()) + .WillRepeatedly(Return(kTestJid)); + + host_change_notification_listener_.reset(new HostChangeNotificationListener( + &mock_listener_, kHostId, &signal_strategy_)); + } + + virtual void TearDown() OVERRIDE { + host_change_notification_listener_.reset(); + EXPECT_TRUE(signal_strategy_listeners_.empty()); + } + + scoped_ptr<XmlElement> GetNotificationStanza(std::string operation, + std::string hostId, + std::string botJid) { + scoped_ptr<XmlElement> stanza(new XmlElement(buzz::QN_IQ)); + stanza->AddAttr(QName("", "type"), "set"); + XmlElement* host_changed = new XmlElement( + QName(kChromotingXmlNamespace, "host-changed")); + host_changed->AddAttr(QName(kChromotingXmlNamespace, "operation"), + operation); + host_changed->AddAttr(QName(kChromotingXmlNamespace, "hostid"), hostId); + stanza->AddElement(host_changed); + stanza->AddAttr(buzz::QN_FROM, botJid); + stanza->AddAttr(buzz::QN_TO, kTestJid); + return stanza.Pass(); + } + + MockListener mock_listener_; + MockSignalStrategy signal_strategy_; + std::set<SignalStrategy::Listener*> signal_strategy_listeners_; + scoped_ptr<HostChangeNotificationListener> host_change_notification_listener_; + MessageLoop message_loop_; +}; + +TEST_F(HostChangeNotificationListenerTest, ReceiveValidNotification) { + EXPECT_CALL(mock_listener_, OnHostDeleted()) + .WillOnce(Return()); + scoped_ptr<XmlElement> stanza = GetNotificationStanza( + "delete", kHostId, kChromotingBotJid); + host_change_notification_listener_->OnSignalStrategyIncomingStanza( + stanza.get()); + message_loop_.PostTask(FROM_HERE, base::Bind(MessageLoop::QuitClosure())); + message_loop_.Run(); +} + +TEST_F(HostChangeNotificationListenerTest, ReceiveNotificationBeforeDelete) { + EXPECT_CALL(mock_listener_, OnHostDeleted()) + .Times(0); + scoped_ptr<XmlElement> stanza = GetNotificationStanza( + "delete", kHostId, kChromotingBotJid); + host_change_notification_listener_->OnSignalStrategyIncomingStanza( + stanza.get()); + host_change_notification_listener_.reset(); + message_loop_.PostTask(FROM_HERE, base::Bind(MessageLoop::QuitClosure())); + message_loop_.Run(); +} + + +TEST_F(HostChangeNotificationListenerTest, ReceiveInvalidHostIdNotification) { + EXPECT_CALL(mock_listener_, OnHostDeleted()) + .Times(0); + scoped_ptr<XmlElement> stanza = GetNotificationStanza( + "delete", "1", kChromotingBotJid); + host_change_notification_listener_->OnSignalStrategyIncomingStanza( + stanza.get()); + message_loop_.PostTask(FROM_HERE, base::Bind(MessageLoop::QuitClosure())); + message_loop_.Run(); +} + +TEST_F(HostChangeNotificationListenerTest, ReceiveInvalidBotJidNotification) { + EXPECT_CALL(mock_listener_, OnHostDeleted()) + .Times(0); + scoped_ptr<XmlElement> stanza = GetNotificationStanza( + "delete", kHostId, "notremotingbot@bot.talk.google.com"); + host_change_notification_listener_->OnSignalStrategyIncomingStanza( + stanza.get()); + message_loop_.PostTask(FROM_HERE, base::Bind(MessageLoop::QuitClosure())); + message_loop_.Run(); +} + +TEST_F(HostChangeNotificationListenerTest, ReceiveNonDeleteNotification) { + EXPECT_CALL(mock_listener_, OnHostDeleted()) + .Times(0); + scoped_ptr<XmlElement> stanza = GetNotificationStanza( + "update", kHostId, kChromotingBotJid); + host_change_notification_listener_->OnSignalStrategyIncomingStanza( + stanza.get()); + message_loop_.PostTask(FROM_HERE, base::Bind(MessageLoop::QuitClosure())); + message_loop_.Run(); +} + +} // namespace remoting diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc index f0118d6..21040e5 100644 --- a/remoting/host/remoting_me2me_host.cc +++ b/remoting/host/remoting_me2me_host.cc @@ -51,6 +51,7 @@ #include "remoting/host/dns_blackhole_checker.h" #include "remoting/host/event_executor.h" #include "remoting/host/heartbeat_sender.h" +#include "remoting/host/host_change_notification_listener.h" #include "remoting/host/host_config.h" #include "remoting/host/host_event_logger.h" #include "remoting/host/host_exit_codes.h" @@ -149,6 +150,7 @@ namespace remoting { class HostProcess : public ConfigFileWatcher::Delegate, public HeartbeatSender::Listener, + public HostChangeNotificationListener::Listener, public IPC::Listener, public base::RefCountedThreadSafe<HostProcess> { public: @@ -166,6 +168,9 @@ class HostProcess // HeartbeatSender::Listener overrides. virtual void OnUnknownHostIdError() OVERRIDE; + // HostChangeNotificationListener::Listener overrides. + virtual void OnHostDeleted() OVERRIDE; + private: enum HostState { // Host process has just been started. Waiting for config and policies to be @@ -270,6 +275,8 @@ class HostProcess // Accessed on the UI thread. scoped_ptr<IPC::ChannelProxy> daemon_channel_; + + // Created on the UI thread but used from the network thread. FilePath host_config_path_; scoped_ptr<DesktopEnvironmentFactory> desktop_environment_factory_; @@ -286,7 +293,6 @@ class HostProcess std::string xmpp_login_; std::string xmpp_auth_token_; std::string xmpp_auth_service_; - scoped_ptr<policy_hack::PolicyWatcher> policy_watcher_; bool allow_nat_traversal_; std::string talkgadget_prefix_; @@ -300,6 +306,7 @@ class HostProcess scoped_ptr<XmppSignalStrategy> signal_strategy_; scoped_ptr<SignalingConnector> signaling_connector_; scoped_ptr<HeartbeatSender> heartbeat_sender_; + scoped_ptr<HostChangeNotificationListener> host_change_notification_listener_; scoped_ptr<LogToServer> log_to_server_; scoped_ptr<HostEventLogger> host_event_logger_; @@ -652,6 +659,11 @@ void HostProcess::OnUnknownHostIdError() { ShutdownHost(kInvalidHostIdExitCode); } +void HostProcess::OnHostDeleted() { + LOG(ERROR) << "Host was deleted from the directory."; + ShutdownHost(kInvalidHostIdExitCode); +} + // Applies the host config, returning true if successful. bool HostProcess::ApplyConfig(scoped_ptr<JsonHostConfig> config) { DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); @@ -908,6 +920,9 @@ void HostProcess::StartHost() { heartbeat_sender_.reset(new HeartbeatSender( this, host_id_, signal_strategy_.get(), &key_pair_)); + host_change_notification_listener_.reset(new HostChangeNotificationListener( + this, host_id_, signal_strategy_.get())); + log_to_server_.reset( new LogToServer(host_, ServerLogEntry::ME2ME, signal_strategy_.get())); host_event_logger_ = HostEventLogger::Create(host_, kApplicationName); @@ -1004,6 +1019,7 @@ void HostProcess::ShutdownOnNetworkThread() { host_event_logger_.reset(); log_to_server_.reset(); heartbeat_sender_.reset(); + host_change_notification_listener_.reset(); signaling_connector_.reset(); signal_strategy_.reset(); resizing_host_observer_.reset(); diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index cff8225..7d0a4d4 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -322,6 +322,8 @@ 'host/event_executor_win.cc', 'host/heartbeat_sender.cc', 'host/heartbeat_sender.h', + 'host/host_change_notification_listener.cc', + 'host/host_change_notification_listener.h', 'host/host_config.cc', 'host/host_config.h', 'host/host_exit_codes.h', @@ -2196,6 +2198,7 @@ 'host/desktop_session_agent_posix.cc', 'host/desktop_session_agent_win.cc', 'host/heartbeat_sender_unittest.cc', + 'host/host_change_notification_listener_unittest.cc', 'host/host_key_pair_unittest.cc', 'host/host_mock_objects.cc', 'host/host_mock_objects.h', |