summaryrefslogtreecommitdiffstats
path: root/remoting
diff options
context:
space:
mode:
authorrmsousa@chromium.org <rmsousa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-30 05:10:43 +0000
committerrmsousa@chromium.org <rmsousa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-30 05:10:43 +0000
commit49aa8fa463615a4d4ad345b492fab72aba0f8a27 (patch)
tree53fb835d6b2e51fcc05bd6529352d09492f15260 /remoting
parentdc1f8546772aaaf0460afedd894c0c1c270b8890 (diff)
downloadchromium_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.cc80
-rw-r--r--remoting/host/host_change_notification_listener.h59
-rw-r--r--remoting/host/host_change_notification_listener_unittest.cc147
-rw-r--r--remoting/host/remoting_me2me_host.cc18
-rw-r--r--remoting/remoting.gyp3
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',