summaryrefslogtreecommitdiffstats
path: root/remoting/protocol
diff options
context:
space:
mode:
authorsergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-28 20:59:56 +0000
committersergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-28 20:59:56 +0000
commit2fc3b4267fd4f28dd864c2858cea0e84f55cbb0a (patch)
tree726f9dc89d195927c151dd969c68051b0c135a02 /remoting/protocol
parente393b9fc0267deaf55f3624aa7b0bfb08707dfda (diff)
downloadchromium_src-2fc3b4267fd4f28dd864c2858cea0e84f55cbb0a.zip
chromium_src-2fc3b4267fd4f28dd864c2858cea0e84f55cbb0a.tar.gz
chromium_src-2fc3b4267fd4f28dd864c2858cea0e84f55cbb0a.tar.bz2
Implemented basic support Chromotocol connection.
New code supports multiple PseudoTCP and UDP channels. Client and host still use old JjngleChannel for connection. BUG=53986 TEST=None Review URL: http://codereview.chromium.org/3319021 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@60842 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/protocol')
-rw-r--r--remoting/protocol/chromoting_connection.h61
-rw-r--r--remoting/protocol/chromoting_server.h40
-rw-r--r--remoting/protocol/jingle_chromoting_connection.cc233
-rw-r--r--remoting/protocol/jingle_chromoting_connection.h97
-rw-r--r--remoting/protocol/jingle_chromoting_server.cc216
-rw-r--r--remoting/protocol/jingle_chromoting_server.h111
-rw-r--r--remoting/protocol/protocol_test_client.cc380
7 files changed, 1138 insertions, 0 deletions
diff --git a/remoting/protocol/chromoting_connection.h b/remoting/protocol/chromoting_connection.h
new file mode 100644
index 0000000..e9c0f42d
--- /dev/null
+++ b/remoting/protocol/chromoting_connection.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2010 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_CHROMOTING_CONNECTION_H_
+#define REMOTING_PROTOCOL_CHROMOTING_CONNECTION_H_
+
+#include <string>
+
+#include "base/callback.h"
+
+class Task;
+
+namespace net {
+class Socket;
+} // namespace net
+
+namespace remoting {
+
+// Generic interface for Chromoting connection.
+class ChromotingConnection
+ : public base::RefCountedThreadSafe<ChromotingConnection> {
+ public:
+ enum State {
+ INITIALIZING,
+ CONNECTING,
+ CONNECTED,
+ CLOSED,
+ FAILED,
+ };
+
+ typedef Callback1<State>::Type StateChangeCallback;
+
+ // Set callback that is called when state of the connection is changed.
+ // Must be called on the jingle thread only.
+ virtual void SetStateChangeCallback(StateChangeCallback* callback) = 0;
+
+ // Reliable PseudoTCP channels for this connection.
+ // TODO(sergeyu): Remove VideoChannel, and use RTP channels instead.
+ virtual net::Socket* GetVideoChannel() = 0;
+ virtual net::Socket* GetEventsChannel() = 0;
+
+ // Unreliable channels for this connection.
+ virtual net::Socket* GetVideoRtpChannel() = 0;
+ virtual net::Socket* GetVideoRtcpChannel() = 0;
+
+ // JID of the other side.
+ virtual const std::string& jid() = 0;
+
+ // Closed connection. Callbacks are guaranteed not to be called after
+ // |closed_task| is executed.
+ virtual void Close(Task* closed_task) = 0;
+
+ protected:
+ friend class base::RefCountedThreadSafe<ChromotingConnection>;
+ virtual ~ChromotingConnection() { }
+};
+
+} // namespace remoting
+
+#endif // REMOTING_PROTOCOL_CHROMOTING_CONNECTION_H_
diff --git a/remoting/protocol/chromoting_server.h b/remoting/protocol/chromoting_server.h
new file mode 100644
index 0000000..5b93534
--- /dev/null
+++ b/remoting/protocol/chromoting_server.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2010 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_CHROMOTING_SERVER_H_
+#define REMOTING_PROTOCOL_CHROMOTING_SERVER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/ref_counted.h"
+#include "remoting/protocol/chromoting_connection.h"
+
+class Task;
+
+namespace remoting {
+
+// Generic interface for Chromoting server.
+class ChromotingServer : public base::RefCountedThreadSafe<ChromotingServer> {
+ public:
+ typedef Callback2<ChromotingConnection*, bool*>::Type NewConnectionCallback;
+
+ // Initializes connection to the host |jid|.
+ virtual scoped_refptr<ChromotingConnection> Connect(
+ const std::string& jid,
+ ChromotingConnection::StateChangeCallback* state_change_callback) = 0;
+
+ // Close connection. |close_task| is executed after the session client
+ // is actually closed. No callbacks are called after |closed_task| is
+ // executed.
+ virtual void Close(Task* closed_task) = 0;
+
+ protected:
+ friend class base::RefCountedThreadSafe<ChromotingServer>;
+ virtual ~ChromotingServer() { }
+};
+
+} // namespace remoting
+
+#endif // REMOTING_PROTOCOL_CHROMOTING_SERVER_H_
diff --git a/remoting/protocol/jingle_chromoting_connection.cc b/remoting/protocol/jingle_chromoting_connection.cc
new file mode 100644
index 0000000..48e3ec0
--- /dev/null
+++ b/remoting/protocol/jingle_chromoting_connection.cc
@@ -0,0 +1,233 @@
+// Copyright (c) 2010 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/protocol/jingle_chromoting_connection.h"
+
+#include "base/message_loop.h"
+#include "net/base/net_errors.h"
+#include "remoting/jingle_glue/channel_socket_adapter.h"
+#include "remoting/jingle_glue/stream_socket_adapter.h"
+#include "remoting/protocol/jingle_chromoting_server.h"
+#include "third_party/libjingle/source/talk/base/thread.h"
+#include "third_party/libjingle/source/talk/p2p/base/session.h"
+#include "third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.h"
+
+using cricket::BaseSession;
+using cricket::PseudoTcpChannel;
+using cricket::Session;
+
+namespace remoting {
+
+namespace {
+const char kVideoChannelName[] = "video";
+const char kVideoRtpChannelName[] = "videortp";
+const char kVideoRtcpChannelName[] = "videortcp";
+const char kEventsChannelName[] = "events";
+} // namespace
+
+JingleChromotingConnection::JingleChromotingConnection(
+ JingleChromotingServer* session_client)
+ : session_client_(session_client),
+ state_(INITIALIZING),
+ closed_(false),
+ session_(NULL),
+ events_channel_(NULL),
+ video_channel_(NULL) {
+}
+
+JingleChromotingConnection::~JingleChromotingConnection() {
+ DCHECK(closed_);
+}
+
+void JingleChromotingConnection::Init(Session* session) {
+ DCHECK_EQ(session_client_->message_loop(), MessageLoop::current());
+
+ session_ = session;
+ jid_ = session_->remote_name();
+ session_->SignalState.connect(
+ this, &JingleChromotingConnection::OnSessionState);
+}
+
+bool JingleChromotingConnection::HasSession(cricket::Session* session) {
+ return session_ == session;
+}
+
+Session* JingleChromotingConnection::ReleaseSession() {
+ DCHECK_EQ(session_client_->message_loop(), MessageLoop::current());
+
+ SetState(CLOSED);
+ Session* session = session_;
+ if (session_)
+ session_->SignalState.disconnect(this);
+ session_ = NULL;
+ closed_ = true;
+ return session;
+}
+
+void JingleChromotingConnection::SetStateChangeCallback(
+ StateChangeCallback* callback) {
+ DCHECK_EQ(session_client_->message_loop(), MessageLoop::current());
+ DCHECK(callback);
+ state_change_callback_.reset(callback);
+}
+
+// TODO(sergeyu): Remove this method after we switch to RTP.
+net::Socket* JingleChromotingConnection::GetVideoChannel() {
+ DCHECK_EQ(session_client_->message_loop(), MessageLoop::current());
+ return video_channel_adapter_.get();
+}
+
+net::Socket* JingleChromotingConnection::GetEventsChannel() {
+ DCHECK_EQ(session_client_->message_loop(), MessageLoop::current());
+ return events_channel_adapter_.get();
+}
+
+net::Socket* JingleChromotingConnection::GetVideoRtpChannel() {
+ DCHECK_EQ(session_client_->message_loop(), MessageLoop::current());
+ return video_rtp_channel_.get();
+}
+
+net::Socket* JingleChromotingConnection::GetVideoRtcpChannel() {
+ DCHECK_EQ(session_client_->message_loop(), MessageLoop::current());
+ return video_rtcp_channel_.get();
+}
+
+void JingleChromotingConnection::Close(Task* closed_task) {
+ if (MessageLoop::current() != session_client_->message_loop()) {
+ session_client_->message_loop()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &JingleChromotingConnection::Close,
+ closed_task));
+ return;
+ }
+
+ if (!closed_) {
+ if (events_channel_adapter_.get())
+ events_channel_adapter_->Close(net::ERR_CONNECTION_CLOSED);
+
+ if (events_channel_) {
+ events_channel_->OnSessionTerminate(session_);
+ events_channel_ = NULL;
+ }
+
+ if (video_channel_adapter_.get())
+ video_channel_adapter_->Close(net::ERR_CONNECTION_CLOSED);
+
+ if (video_channel_) {
+ video_channel_->OnSessionTerminate(session_);
+ video_channel_ = NULL;
+ }
+
+ if (video_rtp_channel_.get())
+ video_rtp_channel_->Close(net::ERR_CONNECTION_CLOSED);
+ if (video_rtcp_channel_.get())
+ video_rtcp_channel_->Close(net::ERR_CONNECTION_CLOSED);
+
+ if (session_)
+ session_->Terminate();
+
+ closed_ = true;
+ }
+
+ closed_task->Run();
+ delete closed_task;
+}
+
+void JingleChromotingConnection::OnSessionState(
+ BaseSession* session, BaseSession::State state) {
+ DCHECK_EQ(session_, session);
+
+ switch (state) {
+ case Session::STATE_SENTINITIATE:
+ OnInitiate(false);
+ break;
+
+ case Session::STATE_RECEIVEDINITIATE:
+ OnInitiate(true);
+ break;
+
+ case Session::STATE_SENTACCEPT:
+ case Session::STATE_RECEIVEDACCEPT:
+ OnAccept();
+ break;
+
+ case Session::STATE_RECEIVEDTERMINATE:
+ OnTerminate();
+ break;
+
+ case Session::STATE_DEINIT:
+ // Close() must have been called before this.
+ NOTREACHED();
+ break;
+
+ default:
+ break;
+ }
+}
+
+void JingleChromotingConnection::OnInitiate(bool incoming) {
+ jid_ = session_->remote_name();
+ if (incoming)
+ session_client_->AcceptConnection(this, session_);
+ SetState(CONNECTING);
+}
+
+void JingleChromotingConnection::OnAccept() {
+ // Create video RTP channels.
+ video_rtp_channel_.reset(new TransportChannelSocketAdapter(
+ session_->CreateChannel(kVideoRtpChannelName)));
+ video_rtcp_channel_.reset(new TransportChannelSocketAdapter(
+ session_->CreateChannel(kVideoRtcpChannelName)));
+
+ // Create events channel.
+ events_channel_ =
+ new PseudoTcpChannel(talk_base::Thread::Current(), session_);
+ events_channel_->Connect(kEventsChannelName);
+ events_channel_adapter_.reset(new StreamSocketAdapter(
+ events_channel_->GetStream()));
+
+ // Create video channel.
+ // TODO(sergeyu): Remove video channel when we are ready to switch to RTP.
+ video_channel_ =
+ new PseudoTcpChannel(talk_base::Thread::Current(), session_);
+ video_channel_->Connect(kVideoChannelName);
+ video_channel_adapter_.reset(new StreamSocketAdapter(
+ video_channel_->GetStream()));
+
+ SetState(CONNECTED);
+}
+
+void JingleChromotingConnection::OnTerminate() {
+ if (events_channel_adapter_.get())
+ events_channel_adapter_->Close(net::ERR_CONNECTION_ABORTED);
+ if (events_channel_) {
+ events_channel_->OnSessionTerminate(session_);
+ events_channel_ = NULL;
+ }
+
+ if (video_channel_adapter_.get())
+ video_channel_adapter_->Close(net::ERR_CONNECTION_ABORTED);
+ if (video_channel_) {
+ video_channel_->OnSessionTerminate(session_);
+ video_channel_ = NULL;
+ }
+
+ if (video_rtp_channel_.get())
+ video_rtp_channel_->Close(net::ERR_CONNECTION_ABORTED);
+ if (video_rtcp_channel_.get())
+ video_rtcp_channel_->Close(net::ERR_CONNECTION_ABORTED);
+
+ SetState(CLOSED);
+
+ closed_ = true;
+}
+
+void JingleChromotingConnection::SetState(State new_state) {
+ if (new_state != state_) {
+ state_ = new_state;
+ if (!closed_ && state_change_callback_.get())
+ state_change_callback_->Run(new_state);
+ }
+}
+
+} // namespace remoting
diff --git a/remoting/protocol/jingle_chromoting_connection.h b/remoting/protocol/jingle_chromoting_connection.h
new file mode 100644
index 0000000..ada4ead
--- /dev/null
+++ b/remoting/protocol/jingle_chromoting_connection.h
@@ -0,0 +1,97 @@
+// Copyright (c) 2010 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_JINGLE_CHROMOTING_CONNECTION_H_
+#define REMOTING_PROTOCOL_JINGLE_CHROMOTING_CONNECTION_H_
+
+#include "base/lock.h"
+#include "base/ref_counted.h"
+#include "remoting/protocol/chromoting_connection.h"
+#include "third_party/libjingle/source/talk/base/sigslot.h"
+#include "third_party/libjingle/source/talk/p2p/base/session.h"
+
+namespace cricket {
+class PseudoTcpChannel;
+} // namespace cricket
+
+namespace net {
+class Socket;
+} // namespace net
+
+namespace remoting {
+
+class JingleChromotingServer;
+class StreamSocketAdapter;
+class TransportChannelSocketAdapter;
+
+// Implements ChromotingConnection that work over libjingle session (the
+// cricket::Session object is passed to Init() method). Created
+// by JingleChromotingServer for incoming and outgoing connections.
+class JingleChromotingConnection : public ChromotingConnection,
+ public sigslot::has_slots<> {
+ public:
+ explicit JingleChromotingConnection(JingleChromotingServer* client);
+
+ // ChromotingConnection interface.
+ virtual void SetStateChangeCallback(StateChangeCallback* callback) ;
+
+ virtual net::Socket* GetVideoChannel();
+ virtual net::Socket* GetEventsChannel();
+ virtual net::Socket* GetVideoRtpChannel();
+ virtual net::Socket* GetVideoRtcpChannel();
+
+ // No synchronization is needed because jid_ is not changed
+ // after new connection is passed to JingleChromotingServer callback.
+ virtual const std::string& jid() { return jid_; };
+
+ virtual void Close(Task* closed_task);
+
+ protected:
+ virtual ~JingleChromotingConnection();
+
+ private:
+ friend class JingleChromotingServer;
+
+ // Called by JingleChromotingServer.
+ void Init(cricket::Session* session);
+ bool HasSession(cricket::Session* session);
+ cricket::Session* ReleaseSession();
+
+ // Used for Session.SignalState sigslot.
+ void OnSessionState(cricket::BaseSession* session,
+ cricket::BaseSession::State state);
+ void OnInitiate(bool incoming);
+ void OnAccept();
+ void OnTerminate();
+
+ void SetState(State new_state);
+
+ // JingleChromotingServer that created this connection.
+ scoped_refptr<JingleChromotingServer> session_client_;
+
+ State state_;
+ scoped_ptr<StateChangeCallback> state_change_callback_;
+
+ bool closed_;
+
+ // JID of the other side. Set when the connection is initialized,
+ // and never changed after that.
+ std::string jid_;
+
+ // The corresponding libjingle session.
+ cricket::Session* session_;
+
+ cricket::PseudoTcpChannel* events_channel_;
+ scoped_ptr<StreamSocketAdapter> events_channel_adapter_;
+ cricket::PseudoTcpChannel* video_channel_;
+ scoped_ptr<StreamSocketAdapter> video_channel_adapter_;
+ scoped_ptr<TransportChannelSocketAdapter> video_rtp_channel_;
+ scoped_ptr<TransportChannelSocketAdapter> video_rtcp_channel_;
+
+ DISALLOW_COPY_AND_ASSIGN(JingleChromotingConnection);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_PROTOCOL_JINGLE_CHROMOTING_CONNECTION_H_
diff --git a/remoting/protocol/jingle_chromoting_server.cc b/remoting/protocol/jingle_chromoting_server.cc
new file mode 100644
index 0000000..851221f
--- /dev/null
+++ b/remoting/protocol/jingle_chromoting_server.cc
@@ -0,0 +1,216 @@
+// Copyright (c) 2010 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/protocol/jingle_chromoting_server.h"
+
+#include "base/message_loop.h"
+#include "remoting/base/constants.h"
+#include "third_party/libjingle/source/talk/p2p/base/transport.h"
+#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
+
+using cricket::ContentDescription;
+using cricket::SessionDescription;
+using cricket::Session;
+using cricket::SessionManager;
+using buzz::QName;
+
+namespace remoting {
+
+namespace {
+const char kDescriptionTag[] = "description";
+const char kTypeTag[] = "type";
+} // namespace
+
+ChromotingContentDescription::ChromotingContentDescription(
+ const std::string& description)
+ : description_(description) {
+}
+
+JingleChromotingServer::JingleChromotingServer(
+ MessageLoop* message_loop)
+ : message_loop_(message_loop),
+ session_manager_(NULL),
+ allow_local_ips_(false),
+ closed_(false) {
+ DCHECK(message_loop_);
+}
+
+void JingleChromotingServer::Init(
+ const std::string& local_jid,
+ cricket::SessionManager* session_manager,
+ NewConnectionCallback* new_connection_callback) {
+ if (MessageLoop::current() != message_loop()) {
+ message_loop()->PostTask(
+ FROM_HERE, NewRunnableMethod(
+ this, &JingleChromotingServer::Init,
+ local_jid, session_manager, new_connection_callback));
+ return;
+ }
+
+ DCHECK(session_manager);
+ DCHECK(new_connection_callback);
+
+ local_jid_ = local_jid;
+ new_connection_callback_.reset(new_connection_callback);
+ session_manager_ = session_manager;
+ session_manager_->AddClient(kChromotingXmlNamespace, this);
+}
+
+JingleChromotingServer::~JingleChromotingServer() {
+ DCHECK(closed_);
+}
+
+void JingleChromotingServer::Close(Task* closed_task) {
+ if (MessageLoop::current() != message_loop()) {
+ message_loop()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &JingleChromotingServer::Close,
+ closed_task));
+ return;
+ }
+
+ if (!closed_) {
+ // Close all connections.
+ session_manager_->RemoveClient(kChromotingXmlNamespace);
+ while (!connections_.empty()) {
+ Session* session = connections_.front()->ReleaseSession();
+ session_manager_->DestroySession(session);
+ connections_.pop_front();
+ }
+ closed_ = true;
+ }
+
+ closed_task->Run();
+ delete closed_task;
+}
+
+void JingleChromotingServer::set_allow_local_ips(bool allow_local_ips) {
+ allow_local_ips_ = allow_local_ips;
+}
+
+scoped_refptr<ChromotingConnection> JingleChromotingServer::Connect(
+ const std::string& jid,
+ ChromotingConnection::StateChangeCallback* state_change_callback) {
+ // Can be called from any thread.
+ scoped_refptr<JingleChromotingConnection> connection =
+ new JingleChromotingConnection(this);
+ message_loop()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &JingleChromotingServer::DoConnect,
+ connection, jid, state_change_callback));
+ return connection;
+}
+
+void JingleChromotingServer::DoConnect(
+ scoped_refptr<JingleChromotingConnection> connection,
+ const std::string& jid,
+ ChromotingConnection::StateChangeCallback* state_change_callback) {
+ DCHECK_EQ(message_loop(), MessageLoop::current());
+ Session* session = session_manager_->CreateSession(
+ local_jid_, kChromotingXmlNamespace);
+
+ // Initialize connection object before we send initiate stanza.
+ connection->SetStateChangeCallback(state_change_callback);
+ connection->Init(session);
+ connections_.push_back(connection);
+
+ session->Initiate(jid, CreateSessionDescription());
+}
+
+MessageLoop* JingleChromotingServer::message_loop() {
+ return message_loop_;
+}
+
+void JingleChromotingServer::OnSessionCreate(
+ Session* session, bool incoming) {
+ DCHECK_EQ(message_loop(), MessageLoop::current());
+
+ // Allow local connections if neccessary.
+ session->transport()->set_allow_local_ips(allow_local_ips_);
+
+ // If this is an outcoming session, the the connection object is already
+ // created.
+ if (incoming) {
+ JingleChromotingConnection* connection =
+ new JingleChromotingConnection(this);
+ connections_.push_back(connection);
+ connection->Init(session);
+ }
+}
+
+void JingleChromotingServer::OnSessionDestroy(Session* session) {
+ DCHECK_EQ(message_loop(), MessageLoop::current());
+
+ std::list<scoped_refptr<JingleChromotingConnection> >::iterator it;
+ for (it = connections_.begin(); it != connections_.end(); ++it) {
+ if ((*it)->HasSession(session)) {
+ (*it)->ReleaseSession();
+ connections_.erase(it);
+ return;
+ }
+ }
+}
+
+void JingleChromotingServer::AcceptConnection(
+ JingleChromotingConnection* connection,
+ Session* session) {
+ bool accept = false;
+ // Always reject connection if we are closed or there is no callback.
+ if (!closed_ && new_connection_callback_.get())
+ new_connection_callback_->Run(connection, &accept);
+
+ if (accept)
+ session->Accept(CreateSessionDescription());
+ else
+ session->Reject();
+}
+
+bool JingleChromotingServer::ParseContent(
+ const buzz::XmlElement* element,
+ const cricket::ContentDescription** content,
+ cricket::ParseError* error) {
+ if (element->Name() == QName(kChromotingXmlNamespace, kDescriptionTag)) {
+ const buzz::XmlElement* type_elem =
+ element->FirstNamed(QName(kChromotingXmlNamespace, kTypeTag));
+ if (type_elem != NULL) {
+ *content = new ChromotingContentDescription(type_elem->BodyText());
+ return true;
+ }
+ }
+ LOG(ERROR) << "Invalid description: " << element->Str();
+ return false;
+}
+
+// WriteContent creates content description for chromoting session. The
+// description looks as follows:
+// <description xmlns="google:remoting">
+// <type>description_text</type>
+// </description>
+// Currently description_text is always empty.
+//
+// TODO(sergeyu): Add more information to the content description. E.g.
+// protocol version, etc.
+bool JingleChromotingServer::WriteContent(
+ const cricket::ContentDescription* content,
+ buzz::XmlElement** elem,
+ cricket::WriteError* error) {
+ const ChromotingContentDescription* desc =
+ static_cast<const ChromotingContentDescription*>(content);
+
+ QName desc_tag(kChromotingXmlNamespace, kDescriptionTag);
+ buzz::XmlElement* root = new buzz::XmlElement(desc_tag, true);
+ QName type_tag(kChromotingXmlNamespace, kTypeTag);
+ buzz::XmlElement* type_elem = new buzz::XmlElement(type_tag);
+ type_elem->SetBodyText(desc->description());
+ root->AddElement(type_elem);
+ *elem = root;
+ return true;
+}
+
+SessionDescription* JingleChromotingServer::CreateSessionDescription() {
+ SessionDescription* desc = new SessionDescription();
+ desc->AddContent("chromoting", kChromotingXmlNamespace,
+ new ChromotingContentDescription(""));
+ return desc;
+}
+
+} // namespace remoting
diff --git a/remoting/protocol/jingle_chromoting_server.h b/remoting/protocol/jingle_chromoting_server.h
new file mode 100644
index 0000000..7a6fd84
--- /dev/null
+++ b/remoting/protocol/jingle_chromoting_server.h
@@ -0,0 +1,111 @@
+// Copyright (c) 2010 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_JINGLE_CHROMOTING_SERVER_H_
+#define REMOTING_PROTOCOL_JINGLE_CHROMOTING_SERVER_H_
+
+#include <list>
+#include <string>
+
+#include "base/lock.h"
+#include "base/ref_counted.h"
+#include "remoting/protocol/chromoting_server.h"
+#include "remoting/protocol/jingle_chromoting_connection.h"
+#include "third_party/libjingle/source/talk/p2p/base/session.h"
+#include "third_party/libjingle/source/talk/p2p/base/sessionclient.h"
+#include "third_party/libjingle/source/talk/p2p/base/sessiondescription.h"
+
+class MessageLoop;
+
+namespace cricket {
+class SessionManager;
+} // namespace cricket
+
+namespace remoting {
+
+// ContentDescription used for chromoting sessions.
+//
+// TODO(sergeyu): Do we need host_id or some other information in the content
+// description?
+class ChromotingContentDescription : public cricket::ContentDescription {
+ public:
+ ChromotingContentDescription(const std::string& description);
+ const std::string& description() const { return description_; }
+ private:
+ std::string description_;
+};
+
+// This class implements SessionClient for Chromoting sessions. It acts as a
+// server that accepts chromoting connections and can also make new connections
+// to other hosts.
+class JingleChromotingServer
+ : public ChromotingServer,
+ public cricket::SessionClient {
+ public:
+ explicit JingleChromotingServer(MessageLoop* message_loop);
+
+ // Initializes the session client. Doesn't accept ownership of the
+ // |session_manager|. Close() must be called _before_ the |session_manager|
+ // is destroyed.
+ virtual void Init(const std::string& local_jid,
+ cricket::SessionManager* session_manager,
+ NewConnectionCallback* new_connection_callback);
+
+ // ChromotingServer interface.
+ virtual scoped_refptr<ChromotingConnection> Connect(
+ const std::string& jid,
+ ChromotingConnection::StateChangeCallback* state_change_callback);
+ virtual void Close(Task* closed_task);
+
+ void set_allow_local_ips(bool allow_local_ips);
+
+ protected:
+ virtual ~JingleChromotingServer();
+
+ private:
+ friend class JingleChromotingConnection;
+
+ // Message loop used by this session client.
+ MessageLoop* message_loop();
+
+ // Called by JingleChromotingConnection when a new connection is initiated.
+ void AcceptConnection(JingleChromotingConnection* connection,
+ cricket::Session* session);
+
+ void DoConnect(
+ scoped_refptr<JingleChromotingConnection> connection,
+ const std::string& jid,
+ ChromotingConnection::StateChangeCallback* state_change_callback);
+
+ // Creates outgoing session description for an incoming connection.
+ cricket::SessionDescription* CreateSessionDescription();
+
+ // cricket::SessionClient interface.
+ virtual void OnSessionCreate(cricket::Session* session,
+ bool received_initiate);
+ virtual void OnSessionDestroy(cricket::Session* session);
+
+ virtual bool ParseContent(const buzz::XmlElement* elem,
+ const cricket::ContentDescription** content,
+ cricket::ParseError* error);
+ virtual bool WriteContent(const cricket::ContentDescription* content,
+ buzz::XmlElement** elem,
+ cricket::WriteError* error);
+
+ std::string local_jid_;
+ MessageLoop* message_loop_;
+ cricket::SessionManager* session_manager_;
+ scoped_ptr<NewConnectionCallback> new_connection_callback_;
+ bool allow_local_ips_;
+
+ bool closed_;
+
+ std::list<scoped_refptr<JingleChromotingConnection> > connections_;
+
+ DISALLOW_COPY_AND_ASSIGN(JingleChromotingServer);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_PROTOCOL_JINGLE_CHROMOTING_SERVER_H_
diff --git a/remoting/protocol/protocol_test_client.cc b/remoting/protocol/protocol_test_client.cc
new file mode 100644
index 0000000..804b008
--- /dev/null
+++ b/remoting/protocol/protocol_test_client.cc
@@ -0,0 +1,380 @@
+// Copyright (c) 2010 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 "build/build_config.h"
+
+#if !defined(OS_WIN)
+extern "C" {
+#include <unistd.h>
+}
+#endif // !defined(OS_WIN)
+
+#include <iostream>
+#include <list>
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/nss_util.h"
+#include "base/time.h"
+#include "net/base/completion_callback.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/socket/socket.h"
+#include "remoting/base/constants.h"
+#include "remoting/jingle_glue/jingle_client.h"
+#include "remoting/jingle_glue/jingle_thread.h"
+#include "remoting/protocol/jingle_chromoting_server.h"
+
+using remoting::kChromotingTokenServiceName;
+
+namespace remoting {
+
+namespace {
+const int kBufferSize = 4096;
+} // namespace
+
+class ProtocolTestClient;
+
+class ProtocolTestConnection
+ : public base::RefCountedThreadSafe<ProtocolTestConnection> {
+ public:
+ ProtocolTestConnection(ProtocolTestClient* client, MessageLoop* message_loop)
+ : client_(client),
+ message_loop_(message_loop),
+ connection_(NULL),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ write_cb_(this, &ProtocolTestConnection::OnWritten)),
+ pending_write_(false),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ read_cb_(this, &ProtocolTestConnection::OnRead)),
+ closed_event_(true, false) {
+ }
+
+ void Init(ChromotingConnection* connection);
+ void Write(const std::string& str);
+ void Read();
+ void Close();
+
+ // ChromotingConnection::Callback interface.
+ virtual void OnStateChange(ChromotingConnection::State state);
+ private:
+ void DoWrite(scoped_refptr<net::IOBuffer> buf, int size);
+ void DoRead();
+
+ void HandleReadResult(int result);
+
+ void OnWritten(int result);
+ void OnRead(int result);
+
+ void OnFinishedClosing();
+
+ ProtocolTestClient* client_;
+ MessageLoop* message_loop_;
+ scoped_refptr<ChromotingConnection> connection_;
+ net::CompletionCallbackImpl<ProtocolTestConnection> write_cb_;
+ bool pending_write_;
+ net::CompletionCallbackImpl<ProtocolTestConnection> read_cb_;
+ scoped_refptr<net::IOBuffer> read_buffer_;
+ base::WaitableEvent closed_event_;
+};
+
+class ProtocolTestClient
+ : public JingleClient::Callback,
+ public base::RefCountedThreadSafe<ProtocolTestClient> {
+ public:
+ ProtocolTestClient()
+ : closed_event_(true, false) {
+ }
+
+ virtual ~ProtocolTestClient() {}
+
+ void Run(const std::string& username, const std::string& auth_token,
+ const std::string& host_jid);
+
+ void OnConnectionClosed(ProtocolTestConnection* connection);
+
+ // JingleClient::Callback interface.
+ virtual void OnStateChange(JingleClient* client, JingleClient::State state);
+ virtual bool OnAcceptConnection(JingleClient* client, const std::string& jid,
+ JingleChannel::Callback** callback);
+ virtual void OnNewConnection(JingleClient* client,
+ scoped_refptr<JingleChannel> channel);
+
+ // callback for JingleChromotingServer interface.
+ virtual void OnNewChromotocolConnection(ChromotingConnection* connection,
+ bool* accept);
+
+ private:
+ typedef std::list<scoped_refptr<ProtocolTestConnection> > ConnectionsList;
+
+ void OnFinishedClosing();
+ void DestroyConnection(scoped_refptr<ProtocolTestConnection> connection);
+
+ std::string host_jid_;
+ scoped_refptr<JingleClient> client_;
+ scoped_refptr<JingleChromotingServer> server_;
+ ConnectionsList connections_;
+ Lock connections_lock_;
+ base::WaitableEvent closed_event_;
+};
+
+
+void ProtocolTestConnection::Init(ChromotingConnection* connection) {
+ connection_ = connection;
+}
+
+void ProtocolTestConnection::Write(const std::string& str) {
+ if (str.empty())
+ return;
+
+ scoped_refptr<net::IOBuffer> buf = new net::IOBuffer(str.length());
+ memcpy(buf->data(), str.c_str(), str.length());
+ message_loop_->PostTask(
+ FROM_HERE, NewRunnableMethod(
+ this, &ProtocolTestConnection::DoWrite, buf, str.length()));
+}
+
+void ProtocolTestConnection::DoWrite(
+ scoped_refptr<net::IOBuffer> buf, int size) {
+ if (pending_write_) {
+ LOG(ERROR) << "Cannot write because there is another pending write.";
+ return;
+ }
+
+ net::Socket* channel = connection_->GetEventsChannel();
+ if (channel != NULL) {
+ int result = channel->Write(buf, size, &write_cb_);
+ if (result < 0) {
+ if (result == net::ERR_IO_PENDING)
+ pending_write_ = true;
+ else
+ LOG(ERROR) << "Write() returned error " << result;
+ }
+ } else {
+ LOG(ERROR) << "Cannot write because the channel isn't intialized yet.";
+ }
+}
+
+void ProtocolTestConnection::Read() {
+ message_loop_->PostTask(
+ FROM_HERE, NewRunnableMethod(
+ this, &ProtocolTestConnection::DoRead));
+}
+
+void ProtocolTestConnection::DoRead() {
+ read_buffer_ = new net::IOBuffer(kBufferSize);
+ while (true) {
+ int result = connection_->GetEventsChannel()->Read(
+ read_buffer_, kBufferSize, &read_cb_);
+ if (result < 0) {
+ if (result != net::ERR_IO_PENDING)
+ LOG(ERROR) << "Read failed: " << result;
+ break;
+ } else {
+ HandleReadResult(result);
+ }
+ }
+}
+
+void ProtocolTestConnection::Close() {
+ connection_->Close(
+ NewRunnableMethod(this, &ProtocolTestConnection::OnFinishedClosing));
+ closed_event_.Wait();
+}
+
+void ProtocolTestConnection::OnFinishedClosing() {
+ closed_event_.Signal();
+}
+
+void ProtocolTestConnection::OnStateChange(
+ ChromotingConnection::State state) {
+ LOG(INFO) << "State of " << connection_->jid() << " changed to " << state;
+ if (state == ChromotingConnection::CONNECTED) {
+ // Start reading after we've connected.
+ Read();
+ } else if (state == ChromotingConnection::CLOSED) {
+ std::cerr << "Connection to " << connection_->jid()
+ << " closed" << std::endl;
+ client_->OnConnectionClosed(this);
+ }
+}
+
+void ProtocolTestConnection::OnWritten(int result) {
+ pending_write_ = false;
+ if (result < 0)
+ LOG(ERROR) << "Write() returned error " << result;
+}
+
+void ProtocolTestConnection::OnRead(int result) {
+ HandleReadResult(result);
+ DoRead();
+}
+
+void ProtocolTestConnection::HandleReadResult(int result) {
+ if (result > 0) {
+ std::string str(reinterpret_cast<const char*>(read_buffer_->data()),
+ result);
+ std::cout << "(" << connection_->jid() << "): " << str << std::endl;
+ } else {
+ LOG(ERROR) << "Read() returned error " << result;
+ }
+}
+
+void ProtocolTestClient::Run(const std::string& username,
+ const std::string& auth_token,
+ const std::string& host_jid) {
+ remoting::JingleThread jingle_thread;
+ jingle_thread.Start();
+ client_ = new JingleClient(&jingle_thread);
+ client_->Init(username, auth_token, kChromotingTokenServiceName, this);
+
+ server_ = new JingleChromotingServer(jingle_thread.message_loop());
+
+ host_jid_ = host_jid;
+
+ while (true) {
+ std::string line;
+ std::getline(std::cin, line);
+
+ {
+ AutoLock auto_lock(connections_lock_);
+
+ // Broadcast message to all clients.
+ for (ConnectionsList::iterator it = connections_.begin();
+ it != connections_.end(); ++it) {
+ (*it)->Write(line);
+ }
+ }
+
+ if (line == "exit")
+ break;
+ }
+
+ while (!connections_.empty()) {
+ connections_.front()->Close();
+ connections_.pop_front();
+ }
+
+ if (server_) {
+ server_->Close(
+ NewRunnableMethod(this, &ProtocolTestClient::OnFinishedClosing));
+ closed_event_.Wait();
+ }
+
+ client_->Close();
+ jingle_thread.Stop();
+}
+
+void ProtocolTestClient::OnConnectionClosed(
+ ProtocolTestConnection* connection) {
+ client_->message_loop()->PostTask(
+ FROM_HERE, NewRunnableMethod(
+ this, &ProtocolTestClient::DestroyConnection,
+ scoped_refptr<ProtocolTestConnection>(connection)));
+}
+
+void ProtocolTestClient::OnStateChange(
+ JingleClient* client, JingleClient::State state) {
+ if (state == JingleClient::CONNECTED) {
+ std::cerr << "Connected as " << client->GetFullJid() << std::endl;
+
+ server_->Init(
+ client_->GetFullJid(), client_->session_manager(),
+ NewCallback(this,
+ &ProtocolTestClient::OnNewChromotocolConnection));
+ server_->set_allow_local_ips(true);
+
+ if (host_jid_ != "") {
+ ProtocolTestConnection* connection =
+ new ProtocolTestConnection(this, client_->message_loop());
+ connection->Init(server_->Connect(
+ host_jid_, NewCallback(connection,
+ &ProtocolTestConnection::OnStateChange)));
+ connections_.push_back(connection);
+ }
+ } else if (state == JingleClient::CLOSED) {
+ std::cerr << "Connection closed" << std::endl;
+ }
+}
+
+bool ProtocolTestClient::OnAcceptConnection(
+ JingleClient* client, const std::string& jid,
+ JingleChannel::Callback** callback) {
+ return false;
+}
+
+void ProtocolTestClient::OnNewConnection(
+ JingleClient* client, scoped_refptr<JingleChannel> channel) {
+ NOTREACHED();
+}
+
+void ProtocolTestClient::OnNewChromotocolConnection(
+ ChromotingConnection* connection, bool* accept) {
+ std::cerr << "Accepting connection from " << connection->jid() << std::endl;
+ ProtocolTestConnection* test_connection =
+ new ProtocolTestConnection(this, client_->message_loop());
+ connection->SetStateChangeCallback(
+ NewCallback(test_connection, &ProtocolTestConnection::OnStateChange));
+ test_connection->Init(connection);
+ AutoLock auto_lock(connections_lock_);
+ connections_.push_back(test_connection);
+ *accept = true;
+}
+
+void ProtocolTestClient::OnFinishedClosing() {
+ closed_event_.Signal();
+}
+
+void ProtocolTestClient::DestroyConnection(
+ scoped_refptr<ProtocolTestConnection> connection) {
+ connection->Close();
+ AutoLock auto_lock(connections_lock_);
+ for (ConnectionsList::iterator it = connections_.begin();
+ it != connections_.end(); ++it) {
+ if ((*it) == connection) {
+ connections_.erase(it);
+ return;
+ }
+ }
+}
+
+} // namespace remoting
+
+using remoting::ProtocolTestClient;
+
+void usage(char* command) {
+ std::cerr << "Usage: " << command << "--username=<username>" << std::endl
+ << "\t--auth_token=<auth_token>" << std::endl
+ << "\t[--host_jid=<host_jid>]" << std::endl;
+ exit(1);
+}
+
+int main(int argc, char** argv) {
+ CommandLine::Init(argc, argv);
+ const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
+
+ if (!cmd_line->args().empty() || cmd_line->HasSwitch("help"))
+ usage(argv[0]);
+
+ base::AtExitManager exit_manager;
+
+ base::EnsureNSPRInit();
+ base::EnsureNSSInit();
+
+ std::string host_jid(cmd_line->GetSwitchValueASCII("host_jid"));
+
+ if (!cmd_line->HasSwitch("username"))
+ usage(argv[0]);
+ std::string username(cmd_line->GetSwitchValueASCII("username"));
+
+ if (!cmd_line->HasSwitch("auth_token"))
+ usage(argv[0]);
+ std::string auth_token(cmd_line->GetSwitchValueASCII("auth_token"));
+
+ scoped_refptr<ProtocolTestClient> client = new ProtocolTestClient();
+
+ client->Run(username, auth_token, host_jid);
+
+ return 0;
+}