summaryrefslogtreecommitdiffstats
path: root/remoting
diff options
context:
space:
mode:
Diffstat (limited to 'remoting')
-rw-r--r--remoting/host/chromoting_host.cc24
-rw-r--r--remoting/host/chromoting_host.h12
-rw-r--r--remoting/host/client_session.cc32
-rw-r--r--remoting/host/client_session.h22
-rw-r--r--remoting/host/client_session_unittest.cc201
-rw-r--r--remoting/host/host_extension.h40
-rw-r--r--remoting/host/host_extension_session.h33
-rw-r--r--remoting/host/host_mock_objects.h1
-rw-r--r--remoting/protocol/session_config.cc2
-rw-r--r--remoting/remoting_host.gypi2
10 files changed, 365 insertions, 4 deletions
diff --git a/remoting/host/chromoting_host.cc b/remoting/host/chromoting_host.cc
index ef2ea19..942e643 100644
--- a/remoting/host/chromoting_host.cc
+++ b/remoting/host/chromoting_host.cc
@@ -141,6 +141,10 @@ void ChromotingHost::RemoveStatusObserver(HostStatusObserver* observer) {
status_observers_.RemoveObserver(observer);
}
+void ChromotingHost::AddExtension(scoped_ptr<HostExtension> extension) {
+ extensions_.push_back(extension.release());
+}
+
void ChromotingHost::RejectAuthenticatingClient() {
DCHECK(authenticating_client_);
reject_authenticating_client_ = true;
@@ -230,6 +234,19 @@ void ChromotingHost::OnSessionChannelsConnected(ClientSession* client) {
OnClientConnected(client->client_jid()));
}
+void ChromotingHost::OnSessionClientCapabilities(ClientSession* client) {
+ DCHECK(CalledOnValidThread());
+
+ // Create extension sessions from each registered extension for this client.
+ for (HostExtensionList::iterator extension = extensions_.begin();
+ extension != extensions_.end(); ++extension) {
+ scoped_ptr<HostExtensionSession> extension_session =
+ (*extension)->CreateExtensionSession(client);
+ if (extension_session)
+ client->AddExtensionSession(extension_session.Pass());
+ }
+}
+
void ChromotingHost::OnSessionAuthenticationFailed(ClientSession* client) {
DCHECK(CalledOnValidThread());
@@ -320,6 +337,13 @@ void ChromotingHost::OnIncomingSession(
desktop_environment_factory_,
max_session_duration_,
pairing_registry_);
+
+ // Registers capabilities provided by host extensions.
+ for (HostExtensionList::iterator extension = extensions_.begin();
+ extension != extensions_.end(); ++extension) {
+ client->AddHostCapabilities((*extension)->GetCapabilities());
+ }
+
clients_.push_back(client);
}
diff --git a/remoting/host/chromoting_host.h b/remoting/host/chromoting_host.h
index a6642d4..73e8442 100644
--- a/remoting/host/chromoting_host.h
+++ b/remoting/host/chromoting_host.h
@@ -8,14 +8,16 @@
#include <list>
#include <string>
-#include "base/memory/scoped_ptr.h"
#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/threading/non_thread_safe.h"
#include "base/threading/thread.h"
#include "net/base/backoff_entry.h"
#include "remoting/host/client_session.h"
+#include "remoting/host/host_extension.h"
#include "remoting/host/host_status_monitor.h"
#include "remoting/host/host_status_observer.h"
#include "remoting/protocol/authenticator.h"
@@ -91,6 +93,9 @@ class ChromotingHost : public base::NonThreadSafe,
virtual void AddStatusObserver(HostStatusObserver* observer) OVERRIDE;
virtual void RemoveStatusObserver(HostStatusObserver* observer) OVERRIDE;
+ // Registers a host extension.
+ void AddExtension(scoped_ptr<HostExtension> extension);
+
// This method may be called only from
// HostStatusObserver::OnClientAuthenticated() to reject the new
// client.
@@ -118,6 +123,7 @@ class ChromotingHost : public base::NonThreadSafe,
virtual void OnSessionAuthenticating(ClientSession* client) OVERRIDE;
virtual bool OnSessionAuthenticated(ClientSession* client) OVERRIDE;
virtual void OnSessionChannelsConnected(ClientSession* client) OVERRIDE;
+ virtual void OnSessionClientCapabilities(ClientSession* client) OVERRIDE;
virtual void OnSessionAuthenticationFailed(ClientSession* client) OVERRIDE;
virtual void OnSessionClosed(ClientSession* session) OVERRIDE;
virtual void OnSessionSequenceNumber(ClientSession* session,
@@ -154,6 +160,7 @@ class ChromotingHost : public base::NonThreadSafe,
friend class ChromotingHostTest;
typedef std::list<ClientSession*> ClientList;
+ typedef ScopedVector<HostExtension> HostExtensionList;
// Immediately disconnects all active clients. Host-internal components may
// shutdown asynchronously, but the caller is guaranteed not to receive
@@ -204,6 +211,9 @@ class ChromotingHost : public base::NonThreadSafe,
// The pairing registry for PIN-less authentication.
scoped_refptr<protocol::PairingRegistry> pairing_registry_;
+ // List of host extensions.
+ HostExtensionList extensions_;
+
base::WeakPtrFactory<ChromotingHost> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(ChromotingHost);
diff --git a/remoting/host/client_session.cc b/remoting/host/client_session.cc
index fd1e441..0361932 100644
--- a/remoting/host/client_session.cc
+++ b/remoting/host/client_session.cc
@@ -18,6 +18,7 @@
#include "remoting/host/audio_capturer.h"
#include "remoting/host/audio_scheduler.h"
#include "remoting/host/desktop_environment.h"
+#include "remoting/host/host_extension_session.h"
#include "remoting/host/input_injector.h"
#include "remoting/host/screen_controls.h"
#include "remoting/host/screen_resolution.h"
@@ -98,6 +99,25 @@ ClientSession::~ClientSession() {
connection_.reset();
}
+void ClientSession::AddExtensionSession(
+ scoped_ptr<HostExtensionSession> extension_session) {
+ DCHECK(CalledOnValidThread());
+
+ extension_sessions_.push_back(extension_session.release());
+}
+
+void ClientSession::AddHostCapabilities(const std::string& capabilities) {
+ DCHECK(CalledOnValidThread());
+
+ if (capabilities.empty())
+ return;
+
+ if (!host_capabilities_.empty())
+ host_capabilities_.append(" ");
+
+ host_capabilities_.append(capabilities);
+}
+
void ClientSession::NotifyClientResolution(
const protocol::ClientResolution& resolution) {
DCHECK(CalledOnValidThread());
@@ -168,6 +188,7 @@ void ClientSession::SetCapabilities(
*client_capabilities_ = capabilities.capabilities();
VLOG(1) << "Client capabilities: " << *client_capabilities_;
+ event_handler_->OnSessionClientCapabilities(this);
// Calculate the set of capabilities enabled by both client and host and
// pass it to the desktop environment if it is available.
@@ -204,6 +225,13 @@ void ClientSession::DeliverClientMessage(
HOST_LOG << "gnubby auth is not enabled";
}
return;
+ } else {
+ for(HostExtensionSessionList::iterator it = extension_sessions_.begin();
+ it != extension_sessions_.end(); ++it) {
+ // Extension returns |true| to indicate that the message was handled.
+ if ((*it)->OnExtensionMessage(this, message))
+ return;
+ }
}
}
HOST_LOG << "Unexpected message received: "
@@ -253,7 +281,7 @@ void ClientSession::OnConnectionAuthenticated(
return;
}
- host_capabilities_ = desktop_environment_->GetCapabilities();
+ AddHostCapabilities(desktop_environment_->GetCapabilities());
// Ignore protocol::Capabilities messages from the client if it does not
// support any capabilities.
@@ -261,6 +289,8 @@ void ClientSession::OnConnectionAuthenticated(
VLOG(1) << "The client does not support any capabilities.";
client_capabilities_ = make_scoped_ptr(new std::string());
+ event_handler_->OnSessionClientCapabilities(this);
+
desktop_environment_->SetCapabilities(*client_capabilities_);
}
diff --git a/remoting/host/client_session.h b/remoting/host/client_session.h
index f892329..a86839e 100644
--- a/remoting/host/client_session.h
+++ b/remoting/host/client_session.h
@@ -8,6 +8,7 @@
#include <string>
#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_vector.h"
#include "base/memory/weak_ptr.h"
#include "base/sequenced_task_runner_helpers.h"
#include "base/threading/non_thread_safe.h"
@@ -15,6 +16,8 @@
#include "base/timer/timer.h"
#include "remoting/host/client_session_control.h"
#include "remoting/host/gnubby_auth_handler.h"
+#include "remoting/host/host_extension.h"
+#include "remoting/host/host_extension_session.h"
#include "remoting/host/mouse_clamping_filter.h"
#include "remoting/host/remote_input_filter.h"
#include "remoting/protocol/clipboard_echo_filter.h"
@@ -64,6 +67,9 @@ class ClientSession
// Called after we've finished connecting all channels.
virtual void OnSessionChannelsConnected(ClientSession* client) = 0;
+ // Called after client has reported capabilities.
+ virtual void OnSessionClientCapabilities(ClientSession* client) = 0;
+
// Called after authentication has failed. Must not tear down this
// object. OnSessionClosed() is notified after this handler
// returns.
@@ -103,6 +109,13 @@ class ClientSession
scoped_refptr<protocol::PairingRegistry> pairing_registry);
virtual ~ClientSession();
+ // Adds an extension to client to handle extension messages.
+ void AddExtensionSession(scoped_ptr<HostExtensionSession> extension_session);
+
+ // Adds extended capabilities to advertise to the client, e.g. those
+ // implemented by |DesktopEnvironment| or |HostExtension|s.
+ void AddHostCapabilities(const std::string& capability);
+
// protocol::HostStub interface.
virtual void NotifyClientResolution(
const protocol::ClientResolution& resolution) OVERRIDE;
@@ -148,7 +161,13 @@ class ClientSession
bool is_authenticated() { return auth_input_filter_.enabled(); }
+ const std::string* client_capabilities() const {
+ return client_capabilities_.get();
+ }
+
private:
+ typedef ScopedVector<HostExtensionSession> HostExtensionSessionList;
+
// Creates a proxy for sending clipboard events to the client.
scoped_ptr<protocol::ClipboardStub> CreateClipboardProxy();
@@ -244,6 +263,9 @@ class ClientSession
// Used to proxy gnubby auth traffic.
scoped_ptr<GnubbyAuthHandler> gnubby_auth_handler_;
+ // Host extension sessions, used to handle extension messages.
+ HostExtensionSessionList extension_sessions_;
+
DISALLOW_COPY_AND_ASSIGN(ClientSession);
};
diff --git a/remoting/host/client_session_unittest.cc b/remoting/host/client_session_unittest.cc
index f28050a..d648ba1 100644
--- a/remoting/host/client_session_unittest.cc
+++ b/remoting/host/client_session_unittest.cc
@@ -2,17 +2,24 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <algorithm>
+#include <string>
+#include <vector>
+
#include "base/message_loop/message_loop.h"
+#include "base/strings/string_util.h"
#include "base/test/test_simple_task_runner.h"
#include "remoting/base/auto_thread_task_runner.h"
#include "remoting/base/constants.h"
#include "remoting/host/audio_capturer.h"
#include "remoting/host/client_session.h"
#include "remoting/host/desktop_environment.h"
+#include "remoting/host/host_extension.h"
#include "remoting/host/host_mock_objects.h"
#include "remoting/host/screen_capturer_fake.h"
#include "remoting/protocol/protocol_mock_objects.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
+#include "testing/gmock_mutant.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_region.h"
@@ -31,9 +38,11 @@ using protocol::SessionConfig;
using testing::_;
using testing::AnyNumber;
using testing::AtMost;
+using testing::CreateFunctor;
using testing::DeleteArg;
using testing::DoAll;
using testing::Expectation;
+using testing::Invoke;
using testing::Return;
using testing::ReturnRef;
using testing::Sequence;
@@ -42,6 +51,8 @@ using testing::StrictMock;
namespace {
+const char kDefaultTestCapability[] = "default";
+
ACTION_P2(InjectClipboardEvent, connection, event) {
connection->clipboard_stub()->InjectClipboardEvent(event);
}
@@ -67,8 +78,75 @@ ACTION_P2(DeliverClientMessage, client_session, message) {
client_session->DeliverClientMessage(message);
}
+ACTION_P2(AddHostCapabilities, client_session, capability) {
+ client_session->AddHostCapabilities(capability);
}
+// Matches a |protocol::Capabilities| argument against a list of capabilities
+// formatted as a space-separated string.
+MATCHER_P(EqCapabilities, expected_capabilities, "") {
+ if (!arg.has_capabilities())
+ return false;
+
+ std::vector<std::string> words_args;
+ std::vector<std::string> words_expected;
+ Tokenize(arg.capabilities(), " ", &words_args);
+ Tokenize(expected_capabilities, " ", &words_expected);
+ std::sort(words_args.begin(), words_args.end());
+ std::sort(words_expected.begin(), words_expected.end());
+ return words_args == words_expected;
+}
+
+// |HostExtension| implementation that can handle an extension message type and
+// provide capabilities.
+class FakeExtension : public HostExtension {
+ public:
+ FakeExtension(const std::string& message_type,
+ const std::string& capabilities);
+ virtual ~FakeExtension();
+
+ virtual std::string GetCapabilities() OVERRIDE;
+ virtual scoped_ptr<HostExtensionSession> CreateExtensionSession(
+ ClientSession* client_session) OVERRIDE;
+
+ bool message_handled() {
+ return message_handled_;
+ }
+
+ private:
+ class FakeExtensionSession : public HostExtensionSession {
+ public:
+ FakeExtensionSession(FakeExtension* extension);
+ virtual ~FakeExtensionSession();
+
+ virtual bool OnExtensionMessage(
+ ClientSession* client_session,
+ const protocol::ExtensionMessage& message) OVERRIDE;
+
+ private:
+ FakeExtension* extension_;
+ };
+
+ std::string message_type_;
+ std::string capabilities_;
+ bool message_handled_;
+};
+
+typedef std::vector<HostExtension*> HostExtensionList;
+
+void CreateExtensionSessions(const HostExtensionList& extensions,
+ ClientSession* client_session) {
+ for (HostExtensionList::const_iterator extension = extensions.begin();
+ extension != extensions.end(); ++extension) {
+ scoped_ptr<HostExtensionSession> extension_session =
+ (*extension)->CreateExtensionSession(client_session);
+ if (extension_session)
+ client_session->AddExtensionSession(extension_session.Pass());
+ }
+}
+
+} // namespace
+
class ClientSessionTest : public testing::Test {
public:
ClientSessionTest() : client_jid_("user@domain/rest-of-jid") {}
@@ -100,6 +178,10 @@ class ClientSessionTest : public testing::Test {
// the input pipe line and starts video capturing.
void ConnectClientSession();
+ // Creates expectations to send an extension message and to disconnect
+ // afterwards.
+ void SetSendMessageAndDisconnectExpectation(const std::string& message_type);
+
// Invoked when the last reference to the AutoThreadTaskRunner has been
// released and quits the message loop to finish the test.
void QuitMainMessageLoop();
@@ -131,6 +213,41 @@ class ClientSessionTest : public testing::Test {
scoped_ptr<MockDesktopEnvironmentFactory> desktop_environment_factory_;
};
+FakeExtension::FakeExtension(const std::string& message_type,
+ const std::string& capabilities)
+ : message_type_(message_type),
+ capabilities_(capabilities),
+ message_handled_(false) {
+}
+
+FakeExtension::~FakeExtension() {}
+
+std::string FakeExtension::GetCapabilities() {
+ return capabilities_;
+}
+
+scoped_ptr<HostExtensionSession> FakeExtension::CreateExtensionSession(
+ ClientSession* client_session) {
+ return scoped_ptr<HostExtensionSession>(new FakeExtensionSession(this));
+}
+
+FakeExtension::FakeExtensionSession::FakeExtensionSession(
+ FakeExtension* extension)
+ : extension_(extension) {
+}
+
+FakeExtension::FakeExtensionSession::~FakeExtensionSession() {}
+
+bool FakeExtension::FakeExtensionSession::OnExtensionMessage(
+ ClientSession* client_session,
+ const protocol::ExtensionMessage& message) {
+ if (message.type() == extension_->message_type_) {
+ extension_->message_handled_ = true;
+ return true;
+ }
+ return false;
+}
+
void ClientSessionTest::SetUp() {
// Arrange to run |message_loop_| until no components depend on it.
scoped_refptr<AutoThreadTaskRunner> ui_task_runner = new AutoThreadTaskRunner(
@@ -180,6 +297,11 @@ void ClientSessionTest::SetUp() {
desktop_environment_factory_.get(),
base::TimeDelta(),
NULL));
+
+ // By default, client will report the same capabilities as the host.
+ EXPECT_CALL(client_stub_, SetCapabilities(_))
+ .Times(AtMost(1))
+ .WillOnce(Invoke(client_session_.get(), &ClientSession::SetCapabilities));
}
void ClientSessionTest::TearDown() {
@@ -211,7 +333,8 @@ DesktopEnvironment* ClientSessionTest::CreateDesktopEnvironment() {
EXPECT_CALL(*desktop_environment, CreateVideoCapturerPtr())
.WillOnce(Invoke(this, &ClientSessionTest::CreateVideoCapturer));
EXPECT_CALL(*desktop_environment, GetCapabilities())
- .Times(AtMost(1));
+ .Times(AtMost(1))
+ .WillOnce(Return(kDefaultTestCapability));
EXPECT_CALL(*desktop_environment, SetCapabilities(_))
.Times(AtMost(1));
@@ -232,6 +355,23 @@ void ClientSessionTest::ConnectClientSession() {
client_session_->OnConnectionChannelsConnected(client_session_->connection());
}
+void ClientSessionTest::SetSendMessageAndDisconnectExpectation(
+ const std::string& message_type) {
+ protocol::ExtensionMessage message;
+ message.set_type(message_type);
+ message.set_data("data");
+
+ Expectation authenticated =
+ EXPECT_CALL(session_event_handler_, OnSessionAuthenticated(_))
+ .WillOnce(Return(true));
+ EXPECT_CALL(session_event_handler_, OnSessionChannelsConnected(_))
+ .After(authenticated)
+ .WillOnce(DoAll(
+ DeliverClientMessage(client_session_.get(), message),
+ InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession),
+ InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession)));
+}
+
void ClientSessionTest::QuitMainMessageLoop() {
message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
}
@@ -599,4 +739,63 @@ TEST_F(ClientSessionTest, EnableGnubbyAuth) {
message_loop_.Run();
}
+// Verifies that messages can be handled by extensions.
+TEST_F(ClientSessionTest, ExtensionMessages_MessageHandled) {
+ FakeExtension extension1("ext1", "cap1");
+ FakeExtension extension2("ext2", "cap2");
+ FakeExtension extension3("ext3", "cap3");
+ HostExtensionList extensions;
+ extensions.push_back(&extension1);
+ extensions.push_back(&extension2);
+ extensions.push_back(&extension3);
+
+ EXPECT_CALL(session_event_handler_, OnSessionClientCapabilities(_))
+ .WillOnce(Invoke(CreateFunctor(&CreateExtensionSessions, extensions)));
+
+ SetSendMessageAndDisconnectExpectation("ext2");
+ ConnectClientSession();
+ message_loop_.Run();
+
+ EXPECT_FALSE(extension1.message_handled());
+ EXPECT_TRUE(extension2.message_handled());
+ EXPECT_FALSE(extension3.message_handled());
+}
+
+// Verifies that extension messages not handled by extensions don't result in a
+// crash.
+TEST_F(ClientSessionTest, ExtensionMessages_MessageNotHandled) {
+ FakeExtension extension1("ext1", "cap1");
+ HostExtensionList extensions;
+ extensions.push_back(&extension1);
+
+ EXPECT_CALL(session_event_handler_, OnSessionClientCapabilities(_))
+ .WillOnce(Invoke(CreateFunctor(&CreateExtensionSessions, extensions)));
+
+ SetSendMessageAndDisconnectExpectation("extX");
+ ConnectClientSession();
+ message_loop_.Run();
+
+ EXPECT_FALSE(extension1.message_handled());
+}
+
+TEST_F(ClientSessionTest, ReportCapabilities) {
+ Expectation authenticated =
+ EXPECT_CALL(session_event_handler_, OnSessionAuthenticated(_))
+ .WillOnce(DoAll(
+ AddHostCapabilities(client_session_.get(), "capX capZ"),
+ AddHostCapabilities(client_session_.get(), ""),
+ AddHostCapabilities(client_session_.get(), "capY"),
+ Return(true)));
+ EXPECT_CALL(client_stub_,
+ SetCapabilities(EqCapabilities("capX capY capZ default")));
+ EXPECT_CALL(session_event_handler_, OnSessionChannelsConnected(_))
+ .After(authenticated)
+ .WillOnce(DoAll(
+ InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession),
+ InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession)));
+
+ ConnectClientSession();
+ message_loop_.Run();
+}
+
} // namespace remoting
diff --git a/remoting/host/host_extension.h b/remoting/host/host_extension.h
new file mode 100644
index 0000000..6014f61
--- /dev/null
+++ b/remoting/host/host_extension.h
@@ -0,0 +1,40 @@
+// Copyright 2014 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_EXTENSION_H_
+#define REMOTING_HOST_HOST_EXTENSION_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+
+namespace remoting {
+
+class ClientSession;
+class HostExtensionSession;
+
+// Extends |ChromotingHost| with new functionality, and can use extension
+// messages to communicate with the client.
+class HostExtension {
+ public:
+ virtual ~HostExtension() {}
+
+ // Returns a space-separated list of capabilities provided by this extension.
+ // Capabilities may be used to inform the client of the availability of an
+ // extension. They are merged into the capabilities the host reports to the
+ // client.
+ virtual std::string GetCapabilities() = 0;
+
+ // Creates an extension session, which can handle extension messages for a
+ // client session.
+ // NULL may be returned if |client_session| cannot support this
+ // extension.
+ // |client_session| must outlive the resulting |HostExtensionSession|.
+ virtual scoped_ptr<HostExtensionSession> CreateExtensionSession(
+ ClientSession* client_session) = 0;
+};
+
+} // namespace remoting
+
+#endif // REMOTING_HOST_HOST_EXTENSION_H_
diff --git a/remoting/host/host_extension_session.h b/remoting/host/host_extension_session.h
new file mode 100644
index 0000000..54b9c53
--- /dev/null
+++ b/remoting/host/host_extension_session.h
@@ -0,0 +1,33 @@
+// Copyright 2014 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_EXTENSION_SESSION_H_
+#define REMOTING_HOST_HOST_EXTENSION_SESSION_H_
+
+namespace remoting {
+
+namespace protocol {
+class ExtensionMessage;
+} // namespace protocol
+
+class ClientSession;
+
+// Created by an |HostExtension| to store |ClientSession| specific state, and to
+// handle extension messages.
+class HostExtensionSession {
+ public:
+ virtual ~HostExtensionSession() {}
+
+ // Called when the host receives an |ExtensionMessage| for the |ClientSession|
+ // associated with this |HostExtensionSession|.
+ // It returns |true| if the message was handled, and |false| otherwise.
+ virtual bool OnExtensionMessage(
+ ClientSession* client_session,
+ const protocol::ExtensionMessage& message) = 0;
+};
+
+} // namespace remoting
+
+#endif // REMOTING_HOST_HOST_EXTENSION_SESSION_H_
+
diff --git a/remoting/host/host_mock_objects.h b/remoting/host/host_mock_objects.h
index d916f04..383f474 100644
--- a/remoting/host/host_mock_objects.h
+++ b/remoting/host/host_mock_objects.h
@@ -71,6 +71,7 @@ class MockClientSessionEventHandler : public ClientSession::EventHandler {
MOCK_METHOD1(OnSessionAuthenticating, void(ClientSession* client));
MOCK_METHOD1(OnSessionAuthenticated, bool(ClientSession* client));
MOCK_METHOD1(OnSessionChannelsConnected, void(ClientSession* client));
+ MOCK_METHOD1(OnSessionClientCapabilities, void(ClientSession* client));
MOCK_METHOD1(OnSessionAuthenticationFailed, void(ClientSession* client));
MOCK_METHOD1(OnSessionClosed, void(ClientSession* client));
MOCK_METHOD2(OnSessionSequenceNumber, void(ClientSession* client,
diff --git a/remoting/protocol/session_config.cc b/remoting/protocol/session_config.cc
index 3ce7727..1728a80 100644
--- a/remoting/protocol/session_config.cc
+++ b/remoting/protocol/session_config.cc
@@ -49,7 +49,7 @@ bool SessionConfig::SupportsCapabilities() const {
SessionConfig SessionConfig::ForTest() {
SessionConfig result;
result.set_control_config(ChannelConfig(ChannelConfig::TRANSPORT_MUX_STREAM,
- kControlStreamVersionNoCapabilities,
+ kControlStreamVersion,
ChannelConfig::CODEC_UNDEFINED));
result.set_event_config(ChannelConfig(ChannelConfig::TRANSPORT_MUX_STREAM,
kDefaultStreamVersion,
diff --git a/remoting/remoting_host.gypi b/remoting/remoting_host.gypi
index dc4cdc6..326536f 100644
--- a/remoting/remoting_host.gypi
+++ b/remoting/remoting_host.gypi
@@ -119,6 +119,8 @@
'host/disconnect_window_win.cc',
'host/dns_blackhole_checker.cc',
'host/dns_blackhole_checker.h',
+ 'host/extension.h',
+ 'host/extension_session.h',
'host/gnubby_auth_handler_posix.cc',
'host/gnubby_auth_handler_posix.h',
'host/gnubby_auth_handler_win.cc',