Commit cf233ee7 authored by zijiehe's avatar zijiehe Committed by Commit bot

[Chromoting] Add SessionPlugin in JingleSession

This change adds SessionPlugin interface, and an Session::AddPlugin() function
to attach SessionPlugin instances into Session and its derived classes.
In JingleSession, the plugins will be executed after receiving a message or
before sending a message.
So a SessionPlugin implementation can read fields from and write to a message.
Refer to change https://codereview.chromium.org/2586133002/, it shows how
HostExperimentSessionPlugin works as a SessionPlugin.

This is part of host experiment framework.

BUG=650926

Review-Url: https://codereview.chromium.org/2586403003
Cr-Commit-Position: refs/heads/master@{#441003}
parent f19d9f70
......@@ -146,6 +146,7 @@ static_library("protocol") {
"session_config.cc",
"session_config.h",
"session_manager.h",
"session_plugin.h",
"socket_util.cc",
"socket_util.h",
"spake2_authenticator.cc",
......
......@@ -5,8 +5,10 @@
#include "remoting/protocol/fake_session.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/threading/thread_task_runner_handle.h"
#include "remoting/protocol/fake_authenticator.h"
#include "remoting/protocol/session_plugin.h"
#include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
namespace remoting {
......@@ -102,5 +104,10 @@ void FakeSession::ProcessTransportInfo(
transport_->ProcessTransportInfo(transport_info.get());
}
// TODO(zijiehe): Supports SessionPlugin in FakeSession.
void FakeSession::AddPlugin(SessionPlugin* plugin) {
NOTIMPLEMENTED();
}
} // namespace protocol
} // namespace remoting
......@@ -47,6 +47,7 @@ class FakeSession : public Session {
const SessionConfig& config() override;
void SetTransport(Transport* transport) override;
void Close(ErrorCode error) override;
void AddPlugin(SessionPlugin* plugin) override;
private:
// Callback provided to the |transport_|.
......
......@@ -9,6 +9,7 @@
#include "remoting/base/constants.h"
#include "remoting/protocol/content_description.h"
#include "remoting/protocol/name_value_map.h"
#include "remoting/protocol/session_plugin.h"
#include "remoting/signaling/jid_util.h"
#include "remoting/signaling/remoting_bot.h"
#include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
......@@ -511,6 +512,15 @@ std::unique_ptr<buzz::XmlElement> JingleMessage::ToXml() const {
return root;
}
void JingleMessage::AddAttachment(std::unique_ptr<XmlElement> attachment) {
DCHECK(attachment);
if (!attachments) {
attachments.reset(new XmlElement(
QName(kChromotingXmlNamespace, "attachments")));
}
attachments->AddElement(attachment.release());
}
JingleMessageReply::JingleMessageReply()
: type(REPLY_RESULT),
error_type(NONE) {
......
......@@ -90,6 +90,11 @@ struct JingleMessage {
// message when parsing fails.
bool ParseXml(const buzz::XmlElement* stanza, std::string* error);
// Adds an XmlElement into |attachments|. This function implicitly creates
// |attachments| if it's empty, and |attachment| should not be an empty
// unique_ptr.
void AddAttachment(std::unique_ptr<buzz::XmlElement> attachment);
std::unique_ptr<buzz::XmlElement> ToXml() const;
SignalingAddress from;
......
......@@ -7,9 +7,11 @@
#include <stdint.h>
#include <limits>
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/string_split.h"
......@@ -21,6 +23,7 @@
#include "remoting/protocol/jingle_messages.h"
#include "remoting/protocol/jingle_session_manager.h"
#include "remoting/protocol/session_config.h"
#include "remoting/protocol/session_plugin.h"
#include "remoting/protocol/transport.h"
#include "remoting/signaling/iq_sender.h"
#include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
......@@ -208,14 +211,11 @@ void JingleSession::StartConnection(
session_id_ = base::Uint64ToString(
base::RandGenerator(std::numeric_limits<uint64_t>::max()));
// Send session-initiate message.
std::unique_ptr<JingleMessage> message(new JingleMessage(
peer_address_, JingleMessage::SESSION_INITIATE, session_id_));
message->initiator = session_manager_->signal_strategy_->GetLocalJid();
message->description.reset(new ContentDescription(
session_manager_->protocol_config_->Clone(),
authenticator_->GetNextMessage()));
SendMessage(std::move(message));
// Delay sending session-initiate message to ensure SessionPlugin can be
// attached before the message.
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
base::Bind(&JingleSession::SendSessionInitiateMessage,
weak_factory_.GetWeakPtr()));
SetState(CONNECTING);
}
......@@ -249,6 +249,7 @@ void JingleSession::AcceptIncomingConnection(
const JingleMessage& initiate_message) {
DCHECK(config_);
ProcessIncomingPluginMessage(initiate_message);
// Process the first authentication message.
const buzz::XmlElement* first_auth_message =
initiate_message.description->authenticator_message();
......@@ -322,6 +323,7 @@ void JingleSession::SendTransportInfo(
std::unique_ptr<JingleMessage> message(new JingleMessage(
peer_address_, JingleMessage::TRANSPORT_INFO, session_id_));
message->transport_info = std::move(transport_info);
AddPluginAttachments(message.get());
std::unique_ptr<buzz::XmlElement> stanza = message->ToXml();
stanza->AddAttr(buzz::QN_ID, GetNextOutgoingId());
......@@ -386,9 +388,23 @@ void JingleSession::Close(protocol::ErrorCode error) {
}
}
void JingleSession::AddPlugin(SessionPlugin* plugin) {
DCHECK(plugin);
plugins_.push_back(plugin);
}
void JingleSession::SendMessage(std::unique_ptr<JingleMessage> message) {
DCHECK(thread_checker_.CalledOnValidThread());
if (message->action != JingleMessage::SESSION_TERMINATE) {
// When the host accepts session-initiate message from a client JID it
// doesn't recognize it sends session-terminate without session-accept.
// Attaching plugin information to this session-terminate message may lead
// to privacy issues (e.g. leaking Windows version to someone who does not
// own the host). So a simply approach is to ignore plugins when sending
// SESSION_TERMINATE message.
AddPluginAttachments(message.get());
}
std::unique_ptr<buzz::XmlElement> stanza = message->ToXml();
stanza->AddAttr(buzz::QN_ID, GetNextOutgoingId());
......@@ -482,6 +498,7 @@ void JingleSession::OnTransportInfoResponse(IqRequest* request,
void JingleSession::OnIncomingMessage(const std::string& id,
std::unique_ptr<JingleMessage> message,
const ReplyCallback& reply_callback) {
ProcessIncomingPluginMessage(*message);
std::vector<PendingMessage> ordered = message_queue_->OnIncomingMessage(
id, PendingMessage{std::move(message), reply_callback});
base::WeakPtr<JingleSession> self = weak_factory_.GetWeakPtr();
......@@ -747,6 +764,39 @@ bool JingleSession::is_session_active() {
state_ == AUTHENTICATING || state_ == AUTHENTICATED;
}
void JingleSession::ProcessIncomingPluginMessage(
const JingleMessage& message) {
if (!message.attachments) {
return;
}
for (const auto& plugin : plugins_) {
plugin->OnIncomingMessage(*(message.attachments));
}
}
void JingleSession::AddPluginAttachments(JingleMessage* message) {
DCHECK(message);
for (const auto& plugin : plugins_) {
std::unique_ptr<XmlElement> attachment = plugin->GetNextMessage();
if (attachment) {
message->AddAttachment(std::move(attachment));
}
}
}
void JingleSession::SendSessionInitiateMessage() {
if (state_ != CONNECTING) {
return;
}
std::unique_ptr<JingleMessage> message(new JingleMessage(
peer_address_, JingleMessage::SESSION_INITIATE, session_id_));
message->initiator = session_manager_->signal_strategy_->GetLocalJid();
message->description.reset(new ContentDescription(
session_manager_->protocol_config_->Clone(),
authenticator_->GetNextMessage()));
SendMessage(std::move(message));
}
std::string JingleSession::GetNextOutgoingId() {
return outgoing_id_prefix_ + "_" + base::IntToString(++next_outgoing_id_);
}
......
......@@ -43,6 +43,7 @@ class JingleSession : public Session {
const SessionConfig& config() override;
void SetTransport(Transport* transport) override;
void Close(protocol::ErrorCode error) override;
void AddPlugin(SessionPlugin* plugin) override;
private:
friend class JingleSessionManager;
......@@ -119,6 +120,15 @@ class JingleSession : public Session {
// Returns true if the state of the session is not CLOSED or FAILED
bool is_session_active();
// Executes all plugins against incoming JingleMessage.
void ProcessIncomingPluginMessage(const JingleMessage& message);
// Executes all plugins against outgoing JingleMessage.
void AddPluginAttachments(JingleMessage* message);
// Sends session-initiate message.
void SendSessionInitiateMessage();
// Returns the value of the ID attribute of the next outgoing set IQ with the
// sequence ID encoded.
std::string GetNextOutgoingId();
......@@ -169,6 +179,9 @@ class JingleSession : public Session {
// authenticated.
std::vector<PendingMessage> pending_transport_info_;
// The SessionPlugins attached to this session.
std::vector<SessionPlugin*> plugins_;
base::WeakPtrFactory<JingleSession> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(JingleSession);
......
......@@ -11,6 +11,7 @@
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/test/test_timeouts.h"
#include "base/time/time.h"
......@@ -25,6 +26,7 @@
#include "remoting/protocol/fake_authenticator.h"
#include "remoting/protocol/jingle_session_manager.h"
#include "remoting/protocol/network_settings.h"
#include "remoting/protocol/session_plugin.h"
#include "remoting/protocol/transport.h"
#include "remoting/protocol/transport_context.h"
#include "remoting/signaling/fake_signal_strategy.h"
......@@ -101,6 +103,43 @@ class FakeTransport : public Transport {
base::Closure on_message_callback_;
};
class FakePlugin : public SessionPlugin {
public:
std::unique_ptr<buzz::XmlElement> GetNextMessage() override {
std::string tag_name = "test-tag-";
tag_name += base::IntToString(outgoing_messages_.size());
std::unique_ptr<buzz::XmlElement> new_message(new buzz::XmlElement(
buzz::QName("test-namespace", tag_name)));
outgoing_messages_.push_back(*new_message);
return new_message;
}
void OnIncomingMessage(const buzz::XmlElement& attachments) override {
for (const buzz::XmlElement* it = attachments.FirstElement();
it != nullptr;
it = it->NextElement()) {
incoming_messages_.push_back(*it);
}
}
const std::vector<buzz::XmlElement>& outgoing_messages() const {
return outgoing_messages_;
}
const std::vector<buzz::XmlElement>& incoming_messages() const {
return incoming_messages_;
}
void Clear() {
outgoing_messages_.clear();
incoming_messages_.clear();
}
private:
std::vector<buzz::XmlElement> outgoing_messages_;
std::vector<buzz::XmlElement> incoming_messages_;
};
std::unique_ptr<buzz::XmlElement> CreateTransportInfo(const std::string& id) {
std::unique_ptr<buzz::XmlElement> result(
buzz::XmlElement::ForStr("<transport xmlns='google:remoting:ice'/>"));
......@@ -124,6 +163,7 @@ class JingleSessionTest : public testing::Test {
host_session_.reset(session);
host_session_->SetEventHandler(&host_session_event_handler_);
host_session_->SetTransport(&host_transport_);
host_session_->AddPlugin(&host_plugin_);
}
void DeleteHostSession() { host_session_.reset(); }
......@@ -237,6 +277,7 @@ class JingleSessionTest : public testing::Test {
client_server_->Connect(host_jid_, std::move(authenticator));
client_session_->SetEventHandler(&client_session_event_handler_);
client_session_->SetTransport(&client_transport_);
client_session_->AddPlugin(&client_plugin_);
base::RunLoop().RunUntilIdle();
}
......@@ -258,6 +299,22 @@ class JingleSessionTest : public testing::Test {
.Times(AtLeast(1));
}
void ExpectPluginMessagesEqual() const {
ASSERT_EQ(client_plugin_.outgoing_messages().size(),
host_plugin_.incoming_messages().size());
for (size_t i = 0; i < client_plugin_.outgoing_messages().size(); i++) {
ASSERT_EQ(client_plugin_.outgoing_messages()[i].Str(),
host_plugin_.incoming_messages()[i].Str());
}
ASSERT_EQ(client_plugin_.incoming_messages().size(),
host_plugin_.outgoing_messages().size());
for (size_t i = 0; i < client_plugin_.incoming_messages().size(); i++) {
ASSERT_EQ(client_plugin_.incoming_messages()[i].Str(),
host_plugin_.outgoing_messages()[i].Str());
}
}
std::unique_ptr<base::MessageLoopForIO> message_loop_;
NetworkSettings network_settings_;
......@@ -277,6 +334,9 @@ class JingleSessionTest : public testing::Test {
std::unique_ptr<Session> client_session_;
MockSessionEventHandler client_session_event_handler_;
FakeTransport client_transport_;
FakePlugin host_plugin_;
FakePlugin client_plugin_;
};
......@@ -555,5 +615,38 @@ TEST_F(JingleSessionTest, TransportInfoDuringAuthentication) {
EXPECT_EQ("1", client_transport_.received_messages()[0]->Attr(buzz::QN_ID));
}
TEST_F(JingleSessionTest, TestSessionPlugin) {
host_plugin_.Clear();
client_plugin_.Clear();
CreateSessionManagers(3, FakeAuthenticator::ACCEPT);
ASSERT_NO_FATAL_FAILURE(
InitiateConnection(3, FakeAuthenticator::ACCEPT, false));
ExpectPluginMessagesEqual();
}
TEST_F(JingleSessionTest, SessionPluginShouldNotBeInvolvedInSessionTerminate) {
host_plugin_.Clear();
client_plugin_.Clear();
CreateSessionManagers(1, FakeAuthenticator::REJECT);
InitiateConnection(1, FakeAuthenticator::ACCEPT, true);
// It's expected the client sends one more plugin message than host, the host
// won't send plugin message in the SESSION_TERMINATE message.
ASSERT_EQ(client_plugin_.outgoing_messages().size() - 1,
client_plugin_.incoming_messages().size());
ExpectPluginMessagesEqual();
}
TEST_F(JingleSessionTest, ImmediatelyCloseSessionAfterConnect) {
CreateSessionManagers(3, FakeAuthenticator::ACCEPT);
client_session_ = client_server_->Connect(host_jid_,
base::MakeUnique<FakeAuthenticator>(
FakeAuthenticator::CLIENT, 3, FakeAuthenticator::ACCEPT, true));
client_session_->Close(HOST_OVERLOAD);
base::RunLoop().RunUntilIdle();
// We should only send a SESSION_TERMINATE message if the session has been
// closed before SESSION_INITIATE message.
ASSERT_EQ(1U, host_signal_strategy_->received_messages().size());
}
} // namespace protocol
} // namespace remoting
......@@ -4,10 +4,12 @@
#include "remoting/protocol/protocol_mock_objects.h"
#include <memory>
#include <utility>
#include "base/logging.h"
#include "base/threading/thread_task_runner_handle.h"
#include "remoting/protocol/session_plugin.h"
#include "remoting/protocol/video_stream.h"
namespace remoting {
......
......@@ -203,6 +203,7 @@ class MockSession : public Session {
MOCK_METHOD0(jid, const std::string&());
MOCK_METHOD0(config, const SessionConfig&());
MOCK_METHOD1(Close, void(ErrorCode error));
MOCK_METHOD1(AddPlugin, void(SessionPlugin* plugin));
private:
DISALLOW_COPY_AND_ASSIGN(MockSession);
......
......@@ -5,6 +5,7 @@
#ifndef REMOTING_PROTOCOL_SESSION_H_
#define REMOTING_PROTOCOL_SESSION_H_
#include <memory>
#include <string>
#include "base/macros.h"
......@@ -15,6 +16,7 @@
namespace remoting {
namespace protocol {
class SessionPlugin;
class Transport;
// Session is responsible for initializing and authenticating both incoming and
......@@ -85,6 +87,12 @@ class Session {
// is being closed due to an error.
virtual void Close(ErrorCode error) = 0;
// Adds a SessionPlugin to handle attachments. To ensure plugin attachments
// are processed correctly for session-initiate message, this function must be
// called immediately after SessionManager::Connect() for outgoing connections
// or in the IncomingSessionCallback handler for incoming connections.
virtual void AddPlugin(SessionPlugin* plugin) = 0;
private:
DISALLOW_COPY_AND_ASSIGN(Session);
};
......
// Copyright 2016 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_PROTOCOL_SESSION_PLUGIN_H_
#define REMOTING_PROTOCOL_SESSION_PLUGIN_H_
#include <memory>
#include "base/callback.h"
#include "remoting/protocol/jingle_messages.h"
#include "remoting/protocol/session.h"
#include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
namespace remoting {
namespace protocol {
// Interface for Session plugins. Plugins allow to send and receive optional
// information that is not essential for session handshake. Messages generated
// by the plugins on one end of a connection are attached to the session
// handshake messages and passed to the plugins on the other end. Plugins are
// optional, i.e. Session doesn't need any plugins to connect successfully.
class SessionPlugin {
public:
SessionPlugin() = default;
virtual ~SessionPlugin() = default;
// Returns an XmlElement if the SessionPlugin requires to attach some data
// into the outgoing message.
virtual std::unique_ptr<buzz::XmlElement> GetNextMessage() = 0;
// Handles messages in |attachments|.
virtual void OnIncomingMessage(const buzz::XmlElement& attachments) = 0;
};
} // namespace protocol
} // namespace remoting
#endif // REMOTING_PROTOCOL_SESSION_PLUGIN_H_
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment