diff options
Diffstat (limited to 'remoting/jingle_glue')
-rw-r--r-- | remoting/jingle_glue/iq_request.cc | 79 | ||||
-rw-r--r-- | remoting/jingle_glue/iq_request.h | 65 | ||||
-rw-r--r-- | remoting/jingle_glue/iq_request_unittest.cc | 40 | ||||
-rw-r--r-- | remoting/jingle_glue/jingle_channel.cc | 210 | ||||
-rw-r--r-- | remoting/jingle_glue/jingle_channel.h | 158 | ||||
-rw-r--r-- | remoting/jingle_glue/jingle_channel_unittest.cc | 146 | ||||
-rw-r--r-- | remoting/jingle_glue/jingle_client.cc | 227 | ||||
-rw-r--r-- | remoting/jingle_glue/jingle_client.h | 139 | ||||
-rw-r--r-- | remoting/jingle_glue/jingle_info_task.cc | 132 | ||||
-rw-r--r-- | remoting/jingle_glue/jingle_info_task.h | 45 | ||||
-rw-r--r-- | remoting/jingle_glue/jingle_test_client.cc | 159 | ||||
-rw-r--r-- | remoting/jingle_glue/jingle_thread.cc | 80 | ||||
-rw-r--r-- | remoting/jingle_glue/jingle_thread.h | 70 | ||||
-rw-r--r-- | remoting/jingle_glue/jingle_thread_unittest.cc | 27 | ||||
-rw-r--r-- | remoting/jingle_glue/mock_objects.h | 26 | ||||
-rw-r--r-- | remoting/jingle_glue/relay_port_allocator.cc | 29 | ||||
-rw-r--r-- | remoting/jingle_glue/relay_port_allocator.h | 38 |
17 files changed, 1670 insertions, 0 deletions
diff --git a/remoting/jingle_glue/iq_request.cc b/remoting/jingle_glue/iq_request.cc new file mode 100644 index 0000000..b9fd6bf --- /dev/null +++ b/remoting/jingle_glue/iq_request.cc @@ -0,0 +1,79 @@ +// 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/jingle_glue/iq_request.h" + +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/scoped_ptr.h" +#include "remoting/jingle_glue/jingle_client.h" +#include "talk/xmpp/constants.h" +#include "talk/xmpp/xmppengine.h" + +namespace remoting { + +IqRequest::IqRequest(JingleClient* jingle_client) + : jingle_client_(jingle_client), + cookie_(NULL) { + DCHECK(jingle_client_ != NULL); + DCHECK(MessageLoop::current() == jingle_client_->message_loop()); +} + +IqRequest::~IqRequest() { + DCHECK(MessageLoop::current() == jingle_client_->message_loop()); + Unregister(); +} + +void IqRequest::SendIq(const std::string& type, + const std::string& addressee, + buzz::XmlElement* iq_body) { + DCHECK(MessageLoop::current() == jingle_client_->message_loop()); + + // Unregister the handler if it is already registered. + Unregister(); + + DCHECK(type.length() > 0); + DCHECK(addressee.length() > 0); + + buzz::XmppClient* xmpp_client = jingle_client_->xmpp_client(); + DCHECK(xmpp_client); // Expect that connection is active. + + scoped_ptr<buzz::XmlElement> stanza(MakeIqStanza(type, addressee, iq_body, + xmpp_client->NextId())); + + xmpp_client->engine()->SendIq(stanza.get(), this, &cookie_); +} + +// static +buzz::XmlElement* IqRequest::MakeIqStanza(const std::string& type, + const std::string& addressee, + buzz::XmlElement* iq_body, + const std::string& id) { + buzz::XmlElement* stanza = new buzz::XmlElement(buzz::QN_IQ); + stanza->AddAttr(buzz::QN_TYPE, type); + stanza->AddAttr(buzz::QN_TO, addressee); + stanza->AddAttr(buzz::QN_ID, id); + stanza->AddElement(iq_body); + return stanza; +} + +void IqRequest::Unregister() { + if (cookie_) { + buzz::XmppClient* xmpp_client = jingle_client_->xmpp_client(); + // No need to unregister the handler if the client has been destroyed. + if (xmpp_client) { + xmpp_client->engine()->RemoveIqHandler(cookie_, NULL); + } + cookie_ = NULL; + } +} + +void IqRequest::IqResponse(buzz::XmppIqCookie cookie, + const buzz::XmlElement* stanza) { + if (callback_.get() != NULL) { + callback_->Run(stanza); + } +} + +} // namespace remoting diff --git a/remoting/jingle_glue/iq_request.h b/remoting/jingle_glue/iq_request.h new file mode 100644 index 0000000..1e9e764 --- /dev/null +++ b/remoting/jingle_glue/iq_request.h @@ -0,0 +1,65 @@ +// 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_JINGLE_GLUE_IQ_REQUEST_H_ +#define REMOTING_JINGLE_GLUE_IQ_REQUEST_H_ + +#include <string> + +#include "base/callback.h" +#include "talk/xmpp/xmppengine.h" +#include "testing/gtest/include/gtest/gtest_prod.h" + +namespace remoting { + +class JingleClient; + +// IqRequest class can be used to send an IQ stanza and then receive reply +// stanza for that request. It sends outgoing stanza when SendIq() is called, +// after that it forwards incoming reply stanza to the callback set with +// set_callback(). If multiple IQ stanzas are send with SendIq() then only reply +// to the last one will be received. +// The class must be used on the jingle thread only. +// TODO(sergeyu): Implement unittests for this class. +class IqRequest : private buzz::XmppIqHandler { + public: + typedef Callback1<const buzz::XmlElement*>::Type ReplyCallback; + + explicit IqRequest(JingleClient* jingle_client); + virtual ~IqRequest(); + + // Sends stanza of type |type| to |addressee|. |iq_body| contains body of + // the stanza. Ownership of |iq_body| is transfered to IqRequest. Must + // be called on the jingle thread. + void SendIq(const std::string& type, const std::string& addressee, + buzz::XmlElement* iq_body); + + // Sets callback that is called when reply stanza is received. Callback + // is called on the jingle thread. + void set_callback(ReplyCallback* callback) { + callback_.reset(callback); + } + + private: + FRIEND_TEST(IqRequestTest, MakeIqStanza); + + // XmppIqHandler interface. + virtual void IqResponse(buzz::XmppIqCookie cookie, + const buzz::XmlElement* stanza); + + static buzz::XmlElement* MakeIqStanza(const std::string& type, + const std::string& addressee, + buzz::XmlElement* iq_body, + const std::string& id); + + void Unregister(); + + scoped_refptr<JingleClient> jingle_client_; + buzz::XmppIqCookie cookie_; + scoped_ptr<ReplyCallback> callback_; +}; + +} // namespace remoting + +#endif // REMOTING_JINGLE_GLUE_IQ_REQUEST_H_ diff --git a/remoting/jingle_glue/iq_request_unittest.cc b/remoting/jingle_glue/iq_request_unittest.cc new file mode 100644 index 0000000..88a6033 --- /dev/null +++ b/remoting/jingle_glue/iq_request_unittest.cc @@ -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. + +#include "base/ref_counted.h" +#include "base/string_util.h" +#include "media/base/data_buffer.h" +#include "remoting/jingle_glue/iq_request.h" +#include "talk/xmllite/xmlelement.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace remoting { + +TEST(IqRequestTest, MakeIqStanza) { + const char* kMessageId = "0"; + const char* kNamespace = "chromium:testns"; + const char* kNamespacePrefix = "tes"; + const char* kBodyTag = "test"; + const char* kType = "get"; + const char* kTo = "user@domain.com"; + + std::string expected_xml_string = + StringPrintf( + "<cli:iq type=\"%s\" to=\"%s\" id=\"%s\" " + "xmlns:cli=\"jabber:client\">" + "<%s:%s xmlns:%s=\"%s\"/>" + "</cli:iq>", + kType, kTo, kMessageId, kNamespacePrefix, kBodyTag, + kNamespacePrefix, kNamespace); + + buzz::XmlElement* iq_body = + new buzz::XmlElement(buzz::QName(kNamespace, kBodyTag)); + scoped_ptr<buzz::XmlElement> stanza( + IqRequest::MakeIqStanza(kType, kTo, iq_body, kMessageId)); + + EXPECT_EQ(expected_xml_string, stanza->Str()); +} + +} // namespace remoting diff --git a/remoting/jingle_glue/jingle_channel.cc b/remoting/jingle_glue/jingle_channel.cc new file mode 100644 index 0000000..8111985 --- /dev/null +++ b/remoting/jingle_glue/jingle_channel.cc @@ -0,0 +1,210 @@ +// 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/jingle_glue/jingle_channel.h" + +#include "base/lock.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/waitable_event.h" +#include "media/base/data_buffer.h" +#include "remoting/jingle_glue/jingle_thread.h" +#include "talk/base/stream.h" + +using media::DataBuffer; + +namespace remoting { + +const size_t kReadBufferSize = 4096; + +JingleChannel::JingleChannel(Callback* callback) + : state_(INITIALIZING), + event_handler_(this), + callback_(callback), + write_buffer_size_(0), + current_write_buf_pos_(0) { + DCHECK(callback_ != NULL); +} + +// This constructor is only used in unit test. +JingleChannel::JingleChannel() + : state_(CLOSED), + write_buffer_size_(0), + current_write_buf_pos_(0) { +} + +JingleChannel::~JingleChannel() { + DCHECK(state_ == CLOSED); +} + +void JingleChannel::Init(JingleThread* thread, + talk_base::StreamInterface* stream, + const std::string& jid) { + thread_ = thread; + stream_.reset(stream); + stream_->SignalEvent.connect(&event_handler_, &EventHandler::OnStreamEvent); + + // Initialize |state_|. + switch (stream->GetState()) { + case talk_base::SS_CLOSED: + SetState(CLOSED); + break; + case talk_base::SS_OPENING: + SetState(CONNECTING); + break; + case talk_base::SS_OPEN: + SetState(OPEN); + // Try to read in case there is something in the stream. + thread_->message_loop()->PostTask( + FROM_HERE, NewRunnableMethod(this, &JingleChannel::DoRead)); + break; + default: + NOTREACHED(); + } + + jid_ = jid; +} + +void JingleChannel::Write(scoped_refptr<DataBuffer> data) { + // Discard empty packets. + if (data->GetDataSize() != 0) { + AutoLock auto_lock(write_lock_); + write_queue_.push_back(data); + write_buffer_size_ += data->GetDataSize(); + // Post event so that the data gets written in the tunnel thread. + thread_->message_loop()->PostTask( + FROM_HERE, NewRunnableMethod(this, &JingleChannel::DoWrite)); + } +} + +void JingleChannel::DoRead() { + while (true) { + size_t bytes_to_read; + if (stream_->GetAvailable(&bytes_to_read)) { + // Return immediately if we know there is nothing to read. + if (bytes_to_read == 0) + return; + } else { + // Try to read kReadBufferSize if the stream doesn't support + // GetAvailable(). + bytes_to_read = kReadBufferSize; + } + + scoped_refptr<DataBuffer> buffer( + new DataBuffer(new uint8[bytes_to_read], kReadBufferSize)); + size_t bytes_read; + int error; + talk_base::StreamResult result = stream_->Read( + buffer->GetWritableData(), bytes_to_read, &bytes_read, &error); + switch (result) { + case talk_base::SR_SUCCESS: { + DCHECK(bytes_read > 0); + buffer->SetDataSize(bytes_read); + callback_->OnPacketReceived(this, buffer); + break; + } + case talk_base::SR_BLOCK: { + return; + } + case talk_base::SR_EOS: { + SetState(CLOSED); + return; + } + case talk_base::SR_ERROR: { + SetState(FAILED); + return; + } + } + } +} + +void JingleChannel::DoWrite() { + while (true) { + if (!current_write_buf_) { + AutoLock auto_lock(write_lock_); + if (write_queue_.empty()) + break; + current_write_buf_ = write_queue_.front(); + current_write_buf_pos_ = 0; + write_queue_.pop_front(); + } + + size_t bytes_written; + int error; + talk_base::StreamResult result = stream_->Write( + current_write_buf_->GetData() + current_write_buf_pos_, + current_write_buf_->GetDataSize() - current_write_buf_pos_, + &bytes_written, &error); + switch (result) { + case talk_base::SR_SUCCESS: { + current_write_buf_pos_ += bytes_written; + if (current_write_buf_pos_ >= current_write_buf_->GetDataSize()) + current_write_buf_ = NULL; + { + AutoLock auto_lock(write_lock_); + write_buffer_size_ -= bytes_written; + } + break; + } + case talk_base::SR_BLOCK: { + return; + } + case talk_base::SR_EOS: { + SetState(CLOSED); + return; + } + case talk_base::SR_ERROR: { + SetState(FAILED); + return; + } + } + } +} + +void JingleChannel::OnStreamEvent(talk_base::StreamInterface* stream, + int events, int error) { + if (events & talk_base::SE_OPEN) { + SetState(OPEN); + } + + if (state_ == OPEN && (events & talk_base::SE_WRITE)) { + DoWrite(); + } + + if (state_ == OPEN && (events & talk_base::SE_READ)) { + DoRead(); + } + + if (events & talk_base::SE_CLOSE) { + SetState(CLOSED); + } +} + +void JingleChannel::SetState(State state) { + if (state == state_) + return; + state_ = state; + callback_->OnStateChange(this, state); +} + +void JingleChannel::Close() { + base::WaitableEvent event(true, false); + thread_->message_loop()->PostTask( + FROM_HERE, NewRunnableMethod(this, &JingleChannel::DoClose, &event)); + event.Wait(); +} + +void JingleChannel::DoClose(base::WaitableEvent* done_event) { + if (stream_.get()) + stream_->Close(); + SetState(CLOSED); + done_event->Signal(); +} + +size_t JingleChannel::write_buffer_size() { + AutoLock auto_lock(write_lock_); + return write_buffer_size_; +} + +} // namespace remoting diff --git a/remoting/jingle_glue/jingle_channel.h b/remoting/jingle_glue/jingle_channel.h new file mode 100644 index 0000000..e3f5e51 --- /dev/null +++ b/remoting/jingle_glue/jingle_channel.h @@ -0,0 +1,158 @@ +// 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_JINGLE_GLUE_JINGLE_CHANNEL_H_ +#define REMOTING_JINGLE_GLUE_JINGLE_CHANNEL_H_ + +#include <deque> +#include <string> + +#include "base/basictypes.h" +#include "base/condition_variable.h" +#include "base/lock.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "talk/base/sigslot.h" +#include "testing/gtest/include/gtest/gtest_prod.h" + +namespace base { +class WaitableEvent; +} // namespace base + +namespace talk_base { +class StreamInterface; +} // namespace talk_base + +namespace media { +class Buffer; +class DataBuffer; +} // namespace media + +namespace remoting { +class JingleThread; + +class JingleChannel : public base::RefCountedThreadSafe<JingleChannel> { + public: + enum State { + INITIALIZING, + CONNECTING, + OPEN, + CLOSED, + FAILED, + }; + + class Callback { + public: + virtual ~Callback() {} + + // Called when state of the connection is changed. + virtual void OnStateChange(JingleChannel* channel, State state) = 0; + + // Called when a new packet is received. + virtual void OnPacketReceived(JingleChannel* channel, + scoped_refptr<media::DataBuffer> data) = 0; + }; + + virtual ~JingleChannel(); + + // Puts data to the write buffer. + virtual void Write(scoped_refptr<media::DataBuffer> data); + + // Closes the tunnel. + virtual void Close(); + + // Current state of the tunnel. + State state() { return state_; } + + // JID of the other end of the channel. + const std::string& jid() { return jid_; } + + // Number of bytes currently stored in the write buffer. + size_t write_buffer_size(); + + protected: + // Needs access to constructor, Init(). + friend class JingleClient; + + // Constructor used by unit test only. + // TODO(hclam): Have to suppress warnnings in MSVC. + JingleChannel(); + + // Used by JingleClient to create an instance of the channel. |callback| + // must not be NULL. + JingleChannel(Callback* callback); + + // Initialized the channel. Ownership of the |stream| is transfered to + // caller. Ownership of |thread| is not. + void Init(JingleThread* thread, talk_base::StreamInterface* stream, + const std::string& jid); + void SetState(State state); + + JingleThread* thread_; + scoped_ptr<talk_base::StreamInterface> stream_; + State state_; + + private: + FRIEND_TEST(JingleChannelTest, Init); + FRIEND_TEST(JingleChannelTest, Write); + FRIEND_TEST(JingleChannelTest, Read); + FRIEND_TEST(JingleChannelTest, Close); + + typedef std::deque<scoped_refptr<media::DataBuffer> > DataQueue; + + // Event handler for the stream. It passes stream events from the stream + // to JingleChannel. + class EventHandler : public sigslot::has_slots<> { + protected: + EventHandler(JingleChannel* channel) + : channel_(channel) { } + + // Constructor used only by unit test. + EventHandler() : channel_(NULL) {} + + void OnStreamEvent(talk_base::StreamInterface* stream, + int events, int error) { + channel_->OnStreamEvent(stream, events, error); + } + friend class JingleChannel; + private: + JingleChannel* channel_; + }; + friend class EventHandler; + + // Event handler for the stream. + void OnStreamEvent(talk_base::StreamInterface* stream, + int events, int error); + + // Writes data from the buffer to the stream. Called + // from OnStreamEvent() in the jingle thread. + void DoWrite(); + + // Reads data from the stream and puts it to the read buffer. + // Called from OnStreamEvent() in the jingle thread. + void DoRead(); + + void DoClose(base::WaitableEvent* done_event); + + Callback* callback_; + EventHandler event_handler_; + std::string jid_; + + // Write buffer. |write_lock_| should be locked when accessing |write_queue_| + // and |write_buffer_size_|, but isn't neccessary for |current_write_buf_|. + // |current_write_buf_| is accessed only by the jingle thread. + // |write_buffer_size_| stores number of bytes currently in |write_queue_| + // and in |current_write_buf_|. + DataQueue write_queue_; + size_t write_buffer_size_; + Lock write_lock_; + scoped_refptr<media::DataBuffer> current_write_buf_; + size_t current_write_buf_pos_; + + DISALLOW_COPY_AND_ASSIGN(JingleChannel); +}; + +} // namespace remoting + +#endif // REMOTING_JINGLE_GLUE_JINGLE_CHANNEL_H_ diff --git a/remoting/jingle_glue/jingle_channel_unittest.cc b/remoting/jingle_glue/jingle_channel_unittest.cc new file mode 100644 index 0000000..1a6a01e --- /dev/null +++ b/remoting/jingle_glue/jingle_channel_unittest.cc @@ -0,0 +1,146 @@ +// 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 "base/ref_counted.h" +#include "media/base/data_buffer.h" +#include "remoting/jingle_glue/jingle_channel.h" +#include "remoting/jingle_glue/jingle_thread.h" +#include "talk/base/stream.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::Return; +using testing::Mock; +using testing::SetArgumentPointee; + +namespace remoting { + +namespace { +const size_t kBufferSize = 100; +} // namespace + +class MockCallback : public JingleChannel::Callback { + public: + MOCK_METHOD2(OnStateChange, void(JingleChannel*, JingleChannel::State)); + MOCK_METHOD2(OnPacketReceived, void(JingleChannel*, + scoped_refptr<media::DataBuffer>)); +}; + +class MockStream : public talk_base::StreamInterface { + public: + virtual ~MockStream() {} + MOCK_CONST_METHOD0(GetState, talk_base::StreamState ()); + + MOCK_METHOD4(Read, talk_base::StreamResult (void*, size_t, + size_t*, int*)); + MOCK_METHOD4(Write, talk_base::StreamResult (const void*, size_t, + size_t*, int*)); + MOCK_CONST_METHOD1(GetAvailable, bool (size_t *)); + MOCK_METHOD0(Close, void ()); + + MOCK_METHOD3(PostEvent, void (talk_base::Thread*, int, int)); + MOCK_METHOD2(PostEvent, void (int, int)); +}; + +TEST(JingleChannelTest, Init) { + JingleThread thread; + + MockStream *stream = new MockStream(); + MockCallback callback; + + EXPECT_CALL(*stream, GetState()) + .Times(1) + .WillRepeatedly(Return(talk_base::SS_OPENING)); + + scoped_refptr<JingleChannel> channel = new JingleChannel(&callback); + + EXPECT_CALL(callback, OnStateChange(channel.get(), JingleChannel::CONNECTING)) + .Times(1); + + thread.Start(); + + EXPECT_EQ(JingleChannel::INITIALIZING, channel->state()); + channel->Init(&thread, stream, "user@domain.com"); + EXPECT_EQ(JingleChannel::CONNECTING, channel->state()); + channel->state_ = JingleChannel::CLOSED; + + thread.Stop(); +} + +TEST(JingleChannelTest, Write) { + JingleThread thread; + MockStream* stream = new MockStream(); // Freed by the channel. + MockCallback callback; + + scoped_refptr<media::DataBuffer> data = new media::DataBuffer(kBufferSize); + data->SetDataSize(kBufferSize); + + EXPECT_CALL(*stream, Write(static_cast<const void*>(data->GetData()), + kBufferSize, _, _)) + .WillOnce(DoAll(SetArgumentPointee<2>(kBufferSize), + Return(talk_base::SR_SUCCESS))); + + scoped_refptr<JingleChannel> channel = new JingleChannel(&callback); + + channel->thread_ = &thread; + channel->stream_.reset(stream); + channel->state_ = JingleChannel::OPEN; + thread.Start(); + channel->Write(data); + thread.Stop(); + channel->state_ = JingleChannel::CLOSED; +} + +TEST(JingleChannelTest, Read) { + JingleThread thread; + MockStream* stream = new MockStream(); // Freed by the channel. + MockCallback callback; + + scoped_refptr<media::DataBuffer> data = new media::DataBuffer(kBufferSize); + data->SetDataSize(kBufferSize); + + scoped_refptr<JingleChannel> channel = new JingleChannel(&callback); + + EXPECT_CALL(callback, OnPacketReceived(channel.get(), _)) + .Times(1); + + EXPECT_CALL(*stream, GetAvailable(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(kBufferSize), + Return(true))) + .WillOnce(DoAll(SetArgumentPointee<0>(0), + Return(true))); + + EXPECT_CALL(*stream, Read(_, kBufferSize, _, _)) + .WillOnce(DoAll(SetArgumentPointee<2>(kBufferSize), + Return(talk_base::SR_SUCCESS))); + + channel->thread_ = &thread; + channel->stream_.reset(stream); + channel->state_ = JingleChannel::OPEN; + thread.Start(); + channel->OnStreamEvent(stream, talk_base::SE_READ, 0); + thread.Stop(); + channel->state_ = JingleChannel::CLOSED; +} + +TEST(JingleChannelTest, Close) { + JingleThread thread; + MockStream* stream = new MockStream(); // Freed by the channel. + MockCallback callback; + + EXPECT_CALL(*stream, Close()) + .Times(1); + + scoped_refptr<JingleChannel> channel = new JingleChannel(&callback); + + channel->thread_ = &thread; + channel->stream_.reset(stream); + channel->state_ = JingleChannel::OPEN; + thread.Start(); + channel->Close(); + thread.Stop(); +} + +} // namespace remoting diff --git a/remoting/jingle_glue/jingle_client.cc b/remoting/jingle_glue/jingle_client.cc new file mode 100644 index 0000000..66867f5 --- /dev/null +++ b/remoting/jingle_glue/jingle_client.cc @@ -0,0 +1,227 @@ +// 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/jingle_glue/jingle_client.h" + +#include "base/logging.h" +#include "base/waitable_event.h" +#include "base/message_loop.h" +#include "chrome/common/net/notifier/communicator/xmpp_socket_adapter.h" +#include "remoting/jingle_glue/jingle_thread.h" +#include "remoting/jingle_glue/relay_port_allocator.h" +#include "talk/base/asyncsocket.h" +#include "talk/base/ssladapter.h" +#include "talk/p2p/base/sessionmanager.h" +#include "talk/p2p/client/sessionmanagertask.h" +#ifdef USE_SSL_TUNNEL +#include "talk/session/tunnel/securetunnelsessionclient.h" +#endif +#include "talk/session/tunnel/tunnelsessionclient.h" + +namespace remoting { + +JingleClient::JingleClient() + : callback_(NULL), + state_(START) { } + +JingleClient::~JingleClient() { + // JingleClient can be destroyed only after it's closed. + DCHECK(state_ == CLOSED); +} + +void JingleClient::Init(const std::string& username, + const std::string& password, + Callback* callback) { + DCHECK(username != ""); + DCHECK(callback != NULL); + DCHECK(thread_ == NULL); // Init() can be called only once. + + callback_ = callback; + + username_ = username; + password_ = password; + + thread_.reset(new JingleThread()); + thread_->Start(); + thread_->message_loop()->PostTask( + FROM_HERE, NewRunnableMethod(this, &JingleClient::DoInitialize)); +} + +class JingleClient::ConnectRequest { + public: + ConnectRequest() + : completed_event_(true, false) { } + + JingleChannel* Wait() { + completed_event_.Wait(); + return channel_; + }; + + void Done(JingleChannel* channel) { + channel_ = channel; + completed_event_.Signal(); + }; + + private: + base::WaitableEvent completed_event_; + JingleChannel* channel_; +}; + +JingleChannel* JingleClient::Connect(const std::string& host_jid, + JingleChannel::Callback* callback) { + ConnectRequest request; + thread_->message_loop()->PostTask( + FROM_HERE, NewRunnableMethod(this, &JingleClient::DoConnect, + &request, host_jid, callback)); + return request.Wait(); +} + +void JingleClient::DoConnect(ConnectRequest* request, + const std::string& host_jid, + JingleChannel::Callback* callback) { + talk_base::StreamInterface* stream = + tunnel_session_client_->CreateTunnel(buzz::Jid(host_jid), ""); + DCHECK(stream != NULL); + + JingleChannel* channel = new JingleChannel(callback); + channel->Init(thread_.get(), stream, host_jid); + request->Done(channel); +} + +void JingleClient::Close() { + DCHECK(thread_ != NULL); // Close() be called only after Init(). + message_loop()->PostTask( + FROM_HERE, NewRunnableMethod(this, &JingleClient::DoClose)); + thread_->Stop(); + thread_.reset(NULL); +} + +void JingleClient::DoClose() { + client_->Disconnect(); + // Client is deleted by TaskRunner. + client_ = NULL; + tunnel_session_client_.reset(); + port_allocator_.reset(); + session_manager_.reset(); + network_manager_.reset(); + UpdateState(CLOSED); +} + +void JingleClient::DoInitialize() { + buzz::Jid login_jid(username_); + talk_base::InsecureCryptStringImpl password; + password.password() = password_; + + buzz::XmppClientSettings xcs; + xcs.set_user(login_jid.node()); + xcs.set_host(login_jid.domain()); + xcs.set_resource("chromoting"); + xcs.set_use_tls(true); + xcs.set_pass(talk_base::CryptString(password)); + xcs.set_server(talk_base::SocketAddress("talk.google.com", 5222)); + + client_ = new buzz::XmppClient(thread_->task_pump()); + client_->SignalStateChange.connect( + this, &JingleClient::OnConnectionStateChanged); + + buzz::AsyncSocket* socket = + new notifier::XmppSocketAdapter(xcs, false); + + client_->Connect(xcs, "", socket, NULL); + client_->Start(); + + network_manager_.reset(new talk_base::NetworkManager()); + + RelayPortAllocator* port_allocator = + new RelayPortAllocator(network_manager_.get(), "transp2"); + port_allocator_.reset(port_allocator); + port_allocator->SetJingleInfo(client_); + + session_manager_.reset(new cricket::SessionManager(port_allocator_.get())); +#ifdef USE_SSL_TUNNEL + cricket::SecureTunnelSessionClient* session_client = + new cricket::SecureTunnelSessionClient(client_->jid(), + session_manager_.get()); + if (!session_client->GenerateIdentity()) + return false; + tunnel_session_client_.reset(session_client); +#else // !USE_SSL_TUNNEL + tunnel_session_client_.reset( + new cricket::TunnelSessionClient(client_->jid(), + session_manager_.get())); +#endif // USE_SSL_TUNNEL + + receiver_ = new cricket::SessionManagerTask(client_, session_manager_.get()); + receiver_->EnableOutgoingMessages(); + receiver_->Start(); + + tunnel_session_client_->SignalIncomingTunnel.connect( + this, &JingleClient::OnIncomingTunnel); +} + +std::string JingleClient::GetFullJid() { + AutoLock auto_lock(full_jid_lock_); + return full_jid_; +} + +MessageLoop* JingleClient::message_loop() { + if (thread_ == NULL) { + return NULL; + } + return thread_->message_loop(); +} + +void JingleClient::OnConnectionStateChanged(buzz::XmppEngine::State state) { + switch (state) { + case buzz::XmppEngine::STATE_START: + UpdateState(START); + break; + case buzz::XmppEngine::STATE_OPENING: + UpdateState(CONNECTING); + break; + case buzz::XmppEngine::STATE_OPEN: + { + AutoLock auto_lock(full_jid_lock_); + full_jid_ = client_->jid().Str(); + } + UpdateState(CONNECTED); + break; + case buzz::XmppEngine::STATE_CLOSED: + UpdateState(CLOSED); + break; + } +} + +void JingleClient::OnIncomingTunnel( + cricket::TunnelSessionClient* client, buzz::Jid jid, + std::string description, cricket::Session* session) { + // Decline connection if we don't have callback. + if (!callback_) { + client->DeclineTunnel(session); + return; + } + + JingleChannel::Callback* channel_callback; + if (callback_->OnAcceptConnection(this, jid.Str(), &channel_callback)) { + DCHECK(channel_callback != NULL); + talk_base::StreamInterface* stream = + client->AcceptTunnel(session); + scoped_refptr<JingleChannel> channel(new JingleChannel(channel_callback)); + channel->Init(thread_.get(), stream, jid.Str()); + callback_->OnNewConnection(this, channel); + } else { + client->DeclineTunnel(session); + return; + } +} + +void JingleClient::UpdateState(State new_state) { + if (new_state != state_) { + state_ = new_state; + if (callback_) + callback_->OnStateChange(this, new_state); + } +} + +} // namespace remoting diff --git a/remoting/jingle_glue/jingle_client.h b/remoting/jingle_glue/jingle_client.h new file mode 100644 index 0000000..56d97e1 --- /dev/null +++ b/remoting/jingle_glue/jingle_client.h @@ -0,0 +1,139 @@ +// 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_JINGLE_GLUE_JINGLE_CLIENT_H_ +#define REMOTING_JINGLE_GLUE_JINGLE_CLIENT_H_ + +#include <string> + +#include "remoting/jingle_glue/jingle_channel.h" +#include "talk/xmpp/xmppclient.h" + +class MessageLoop; + +namespace talk_base { +class NetworkManager; +} // namespace talk_base + +namespace cricket { +class BasicPortAllocator; +class SessionManager; +class TunnelSessionClient; +class SessionManagerTask; +class Session; +} // namespace cricket + +namespace remoting { + +class JingleClient : public base::RefCountedThreadSafe<JingleClient>, + public sigslot::has_slots<> { + public: + enum State { + START, // Initial state. + CONNECTING, + CONNECTED, + CLOSED, + }; + + class Callback { + public: + virtual ~Callback() {} + + // Called when state of the connection is changed. + virtual void OnStateChange(JingleClient* client, State state) = 0; + + // Called when a client attempts to connect to the machine. If the + // connection should be accepted, must return true and must set + // channel_callback to the callback for the new channel. + virtual bool OnAcceptConnection( + JingleClient* client, const std::string& jid, + JingleChannel::Callback** channel_callback) = 0; + + // Called when a new client connects to the host. Ownership of the |channel| + // is transfered to the callee. + virtual void OnNewConnection(JingleClient* client, + scoped_refptr<JingleChannel> channel) = 0; + }; + + JingleClient(); + virtual ~JingleClient(); + + // Starts jingle thread and XMPP connection inialization. Must be called + // only once. message_loop() is guaranteed to exist after this method returns, + // but the connection may not be open yet. |callback| specifies callback + // object for the client and must not be NULL. + // TODO(sergeyu): Replace password with a token. + void Init(const std::string& username, const std::string& password, + Callback* callback); + + // Creates new JingleChannel connected to the host with the specified jid. + // The result is returned immediately but the channel fails if the host + // rejects connection. |host_jid| must be a full jid (includes resource ID). + // Ownership of the result is transfered to the caller. The channel must + // be closed/destroyed before JingleClient is destroyed. + JingleChannel* Connect(const std::string& host_jid, + JingleChannel::Callback* callback); + + // Closes XMPP connection and stops the thread. Must be called before the + // object is destroyed. + void Close(); + + // Returns JID with resource ID. Empty string is returned if full JID is not + // known yet, i.e. authentication hasn't finished. + std::string GetFullJid(); + + // Current state of the client. + State state() { return state_; } + + // Returns XmppClient object for the xmpp connection or NULL if not connected. + buzz::XmppClient* xmpp_client() { return client_; } + + // Message loop for the jingle thread or NULL if the thread is not started. + MessageLoop* message_loop(); + + private: + // Used by Connect(). + class ConnectRequest; + + void OnConnectionStateChanged(buzz::XmppEngine::State state); + + void OnIncomingTunnel(cricket::TunnelSessionClient* client, buzz::Jid jid, + std::string description, cricket::Session* session); + + void DoInitialize(); + + // Used by Connect(). + void DoConnect(ConnectRequest* request, + const std::string& host_jid, + JingleChannel::Callback* callback); + + // Used by Close(). + void DoClose(); + + // Updates current state of the connection. Must be called only in + // the jingle thread. + void UpdateState(State new_state); + + buzz::XmppClient* client_; + scoped_ptr<JingleThread> thread_; + State state_; + Callback* callback_; + + std::string username_; + std::string password_; + Lock full_jid_lock_; + std::string full_jid_; + + scoped_ptr<talk_base::NetworkManager> network_manager_; + scoped_ptr<cricket::BasicPortAllocator> port_allocator_; + scoped_ptr<cricket::SessionManager> session_manager_; + scoped_ptr<cricket::TunnelSessionClient> tunnel_session_client_; + cricket::SessionManagerTask* receiver_; + + DISALLOW_COPY_AND_ASSIGN(JingleClient); +}; + +} // namespace remoting + +#endif // REMOTING_JINGLE_GLUE_JINGLE_CLIENT_H_ diff --git a/remoting/jingle_glue/jingle_info_task.cc b/remoting/jingle_glue/jingle_info_task.cc new file mode 100644 index 0000000..c2e6525 --- /dev/null +++ b/remoting/jingle_glue/jingle_info_task.cc @@ -0,0 +1,132 @@ +// 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/jingle_glue/jingle_info_task.h" + +#include "base/scoped_ptr.h" +#include "talk/base/socketaddress.h" +#include "talk/xmpp/constants.h" +#include "talk/xmpp/xmppclient.h" + +namespace remoting { + +// This code is a copy of googleclient/talk/app/jingleinfotask.cc . + +class JingleInfoTask::JingleInfoGetTask : public XmppTask { + public: + explicit JingleInfoGetTask(talk_base::TaskParent* parent) : + XmppTask(parent, buzz::XmppEngine::HL_SINGLE), + done_(false) { } + + virtual int ProcessStart() { + // Set jingle info query IQ stanza. + scoped_ptr<buzz::XmlElement> get_iq( + MakeIq(buzz::STR_GET, buzz::JID_EMPTY, task_id())); + get_iq->AddElement(new buzz::XmlElement(buzz::QN_JINGLE_INFO_QUERY, true)); + if (SendStanza(get_iq.get()) != buzz::XMPP_RETURN_OK) { + return STATE_ERROR; + } + return STATE_RESPONSE; + } + + virtual int ProcessResponse() { + if (done_) { + return STATE_DONE; + } + return STATE_BLOCKED; + } + + protected: + virtual bool HandleStanza(const buzz::XmlElement* stanza) { + if (!MatchResponseIq(stanza, buzz::JID_EMPTY, task_id())) { + return false; + } + + if (stanza->Attr(buzz::QN_TYPE) != buzz::STR_RESULT) { + return false; + } + + // Queue the stanza with the parent so these don't get handled out of order. + JingleInfoTask* parent = static_cast<JingleInfoTask*>(GetParent()); + parent->QueueStanza(stanza); + + // Wake ourselves so we can go into the done state. + done_ = true; + Wake(); + return true; + } + + bool done_; +}; + + +void JingleInfoTask::RefreshJingleInfoNow() { + JingleInfoGetTask* get_task = new JingleInfoGetTask(this); + get_task->Start(); +} + +bool JingleInfoTask::HandleStanza(const buzz::XmlElement* stanza) { + if (!MatchRequestIq(stanza, "set", buzz::QN_JINGLE_INFO_QUERY)) { + return false; + } + + // Only respect relay push from the server. + buzz::Jid from(stanza->Attr(buzz::QN_FROM)); + if (from != buzz::JID_EMPTY && + !from.BareEquals(GetClient()->jid()) && + from != buzz::Jid(GetClient()->jid().domain())) { + return false; + } + + QueueStanza(stanza); + return true; +} + +int JingleInfoTask::ProcessStart() { + std::vector<std::string> relay_hosts; + std::vector<talk_base::SocketAddress> stun_hosts; + std::string relay_token; + const buzz::XmlElement* stanza = NextStanza(); + if (stanza == NULL) { + return STATE_BLOCKED; + } + const buzz::XmlElement* query = + stanza->FirstNamed(buzz::QN_JINGLE_INFO_QUERY); + if (query == NULL) { + return STATE_START; + } + const buzz::XmlElement* stun = query->FirstNamed(buzz::QN_JINGLE_INFO_STUN); + if (stun) { + for (const buzz::XmlElement* server = + stun->FirstNamed(buzz::QN_JINGLE_INFO_SERVER); + server != NULL; + server = server->NextNamed(buzz::QN_JINGLE_INFO_SERVER)) { + std::string host = server->Attr(buzz::QN_JINGLE_INFO_HOST); + std::string port = server->Attr(buzz::QN_JINGLE_INFO_UDP); + if (host != buzz::STR_EMPTY && host != buzz::STR_EMPTY) { + // TODO(sergeyu): Avoid atoi() here. + stun_hosts.push_back( + talk_base::SocketAddress(host, atoi(port.c_str()))); + } + } + } + + const buzz::XmlElement* relay = query->FirstNamed(buzz::QN_JINGLE_INFO_RELAY); + if (relay) { + relay_token = relay->TextNamed(buzz::QN_JINGLE_INFO_TOKEN); + for (const buzz::XmlElement* server = + relay->FirstNamed(buzz::QN_JINGLE_INFO_SERVER); + server != NULL; + server = server->NextNamed(buzz::QN_JINGLE_INFO_SERVER)) { + std::string host = server->Attr(buzz::QN_JINGLE_INFO_HOST); + if (host != buzz::STR_EMPTY) { + relay_hosts.push_back(host); + } + } + } + SignalJingleInfo(relay_token, relay_hosts, stun_hosts); + return STATE_START; +} + +} // namespace remoting diff --git a/remoting/jingle_glue/jingle_info_task.h b/remoting/jingle_glue/jingle_info_task.h new file mode 100644 index 0000000..f02e54e --- /dev/null +++ b/remoting/jingle_glue/jingle_info_task.h @@ -0,0 +1,45 @@ +// 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_JINGLE_GLUE_JINGLE_INFO_TASK_H_ +#define REMOTING_JINGLE_GLUE_JINGLE_INFO_TASK_H_ + +#include <vector> +#include <string> + +#include "talk/base/sigslot.h" +#include "talk/p2p/client/httpportallocator.h" +#include "talk/xmpp/xmppengine.h" +#include "talk/xmpp/xmpptask.h" + +namespace remoting { + +// JingleInfoTask is used to discover addresses of jingle servers. +// See http://code.google.com/apis/talk/jep_extensions/jingleinfo.html +// for more details about the protocol. +// +// This is a copy of googleclient/talk/app/jingleinfotask.h . +class JingleInfoTask : public buzz::XmppTask { + public: + explicit JingleInfoTask(talk_base::TaskParent* parent) + : XmppTask(parent, buzz::XmppEngine::HL_TYPE) {} + + virtual int ProcessStart(); + void RefreshJingleInfoNow(); + + sigslot::signal3<const std::string&, + const std::vector<std::string>&, + const std::vector<talk_base::SocketAddress>&> + SignalJingleInfo; + + protected: + class JingleInfoGetTask; + friend class JingleInfoGetTask; + + virtual bool HandleStanza(const buzz::XmlElement* stanza); +}; + +} // namespace remoting + +#endif // REMOTING_JINGLE_GLUE_JINGLE_INFO_TASK_H_ diff --git a/remoting/jingle_glue/jingle_test_client.cc b/remoting/jingle_glue/jingle_test_client.cc new file mode 100644 index 0000000..38677fe --- /dev/null +++ b/remoting/jingle_glue/jingle_test_client.cc @@ -0,0 +1,159 @@ +// 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 "media/base/data_buffer.h" +#include "remoting/jingle_glue/jingle_channel.h" +#include "remoting/jingle_glue/jingle_client.h" + +using remoting::JingleClient; +using remoting::JingleChannel; + +void SetConsoleEcho(bool on) { +#if defined(OS_WIN) + HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE); + if ((hIn == INVALID_HANDLE_VALUE) || (hIn == NULL)) + return; + + DWORD mode; + if (!GetConsoleMode(hIn, &mode)) + return; + + if (on) { + mode = mode | ENABLE_ECHO_INPUT; + } else { + mode = mode & ~ENABLE_ECHO_INPUT; + } + + SetConsoleMode(hIn, mode); +#else // defined(OS_WIN) + if (on) + system("stty echo"); + else + system("stty -echo"); +#endif // !defined(OS_WIN) +} + +class JingleTestClient : public JingleChannel::Callback, + public JingleClient::Callback { + public: + virtual ~JingleTestClient() {} + + void Run(const std::string& username, const std::string& password, + const std::string& host_jid) { + client_ = new JingleClient(); + client_->Init(username, password, this); + + if (host_jid != "") { + scoped_refptr<JingleChannel> channel = client_->Connect(host_jid, this); + channels_.push_back(channel); + } + + while (true) { + std::string line; + std::getline(std::cin, line); + + { + AutoLock auto_lock(channels_lock_); + + // Broadcast message to all clients. + for (ChannelsList::iterator it = channels_.begin(); + it != channels_.end(); ++it) { + uint8* buf = new uint8[line.length()]; + memcpy(buf, line.c_str(), line.length()); + (*it)->Write(new media::DataBuffer(buf, line.length())); + } + } + + if (line == "exit") + break; + } + + while (!channels_.empty()) { + channels_.front()->Close(); + channels_.pop_front(); + } + + client_->Close(); + } + + // JingleChannel::Callback interface. + void OnStateChange(JingleChannel* channel, JingleChannel::State state) { + LOG(INFO) << "State of " << channel->jid() << " changed to " << state; + } + + void OnPacketReceived(JingleChannel* channel, + scoped_refptr<media::DataBuffer> buffer) { + std::string str(reinterpret_cast<const char*>(buffer->GetData()), + buffer->GetDataSize()); + std::cout << "(" << channel->jid() << "): " << str << std::endl; + } + + // JingleClient::Callback interface. + void OnStateChange(JingleClient* client, JingleClient::State state) { + if (state == JingleClient::CONNECTED) { + std::cerr << "Connected as " << client->GetFullJid() << std::endl; + } else if (state == JingleClient::CLOSED) { + std::cerr << "Connection closed" << std::endl; + } + } + + bool OnAcceptConnection(JingleClient* client, const std::string& jid, + JingleChannel::Callback** callback) { + std::cerr << "Accepting new connection from " << jid << std::endl; + *callback = this; + return true; + } + + void OnNewConnection(JingleClient* client, + scoped_refptr<JingleChannel> channel) { + std::cerr << "Connected to " << channel->jid() << std::endl; + AutoLock auto_lock(channels_lock_); + channels_.push_back(channel); + } + + private: + typedef std::list<scoped_refptr<JingleChannel> > ChannelsList; + + scoped_refptr<JingleClient> client_; + ChannelsList channels_; + Lock channels_lock_; +}; + +int main(int argc, char** argv) { + if (argc > 2) + std::cerr << "Usage: " << argv[0] << " [<host_jid>]" << std::endl; + + base::AtExitManager exit_manager; + + std::string host_jid = argc == 2 ? argv[1] : ""; + + std::string username; + std::cout << "JID: "; + std::cin >> username; + + std::string password; + SetConsoleEcho(false); + std::cout << "Password: "; + std::cin >> password; + SetConsoleEcho(true); + std::cout << std::endl; + + JingleTestClient client; + + client.Run(username, password, host_jid); + + return 0; +} diff --git a/remoting/jingle_glue/jingle_thread.cc b/remoting/jingle_glue/jingle_thread.cc new file mode 100644 index 0000000..5090251 --- /dev/null +++ b/remoting/jingle_glue/jingle_thread.cc @@ -0,0 +1,80 @@ +// 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/jingle_glue/jingle_thread.h" + +#include "base/logging.h" +#include "base/message_loop.h" +#include "talk/base/ssladapter.h" + +namespace remoting { + +const int kRunTasksMessageId = 1; + +TaskPump::TaskPump() { +} + +void TaskPump::WakeTasks() { + talk_base::Thread::Current()->Post(this); +} + +int64 TaskPump::CurrentTime() { + return static_cast<int64>(talk_base::Time()); +} + +void TaskPump::OnMessage(talk_base::Message* pmsg) { + RunTasks(); +} + +JingleThread::JingleThread() + : message_loop_(NULL), + task_pump_(NULL), + started_event_(true, false) { } + +JingleThread::~JingleThread() { } + +void JingleThread::Start() { + Thread::Start(); + started_event_.Wait(); +} + +void JingleThread::Run() { + LOG(INFO) << "Started Jingle thread."; + + MessageLoopForIO message_loop; + message_loop_ = &message_loop; + + TaskPump task_pump; + task_pump_ = &task_pump; + + // Signal after we've initialized |message_loop_| and |task_pump_|. + started_event_.Signal(); + + Post(this, kRunTasksMessageId); + + Thread::Run(); + + message_loop.RunAllPending(); + + task_pump_ = NULL; + message_loop_ = NULL; + + LOG(INFO) << "Jingle thread finished."; +} + +// This method is called every 20ms to process tasks from |message_loop_| +// on this thread. +// TODO(sergeyu): Remove it when JingleThread moved to Chromium's base::Thread. +void JingleThread::PumpAuxiliaryLoops() { + MessageLoop::current()->RunAllPending(); + + // Schedule next execution 20ms from now. + PostDelayed(20, this, kRunTasksMessageId); +} + +void JingleThread::OnMessage(talk_base::Message* msg) { + PumpAuxiliaryLoops(); +} + +} // namespace remoting diff --git a/remoting/jingle_glue/jingle_thread.h b/remoting/jingle_glue/jingle_thread.h new file mode 100644 index 0000000..ecef1fb --- /dev/null +++ b/remoting/jingle_glue/jingle_thread.h @@ -0,0 +1,70 @@ +// 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_JINGLE_GLUE_JINGLE_THREAD_H +#define REMOTING_JINGLE_GLUE_JINGLE_THREAD_H + +#include "base/tracked_objects.h" +#include "base/waitable_event.h" +#include "talk/base/messagequeue.h" +#include "talk/base/taskrunner.h" +#include "talk/base/thread.h" + +class MessageLoop; + +namespace buzz { +class XmppClient; +} + +namespace remoting { + +class TaskPump : public talk_base::MessageHandler, + public talk_base::TaskRunner { + public: + TaskPump(); + + // TaskRunner methods. + void WakeTasks(); + int64 CurrentTime(); + + // MessageHandler methods. + void OnMessage(talk_base::Message* pmsg); +}; + +// TODO(sergeyu): This class should be changed to inherit from Chromiums +// base::Thread instead of libjingle's thread. +class JingleThread : public talk_base::Thread, + private talk_base::MessageHandler { + public: + JingleThread(); + virtual ~JingleThread(); + + void Start(); + + // Main function for the thread. Should not be called directly. + void Run(); + + // Returns Chromiums message loop for this thread. + // TODO(sergeyu): remove this methid when we use base::Thread insted of + // talk_base::Thread + MessageLoop* message_loop() { return message_loop_; } + + // Returns task pump if the thread is running, otherwise NULL is returned. + TaskPump* task_pump() { return task_pump_; } + + private: + virtual void OnMessage(talk_base::Message* msg); + + void PumpAuxiliaryLoops(); + + TaskPump* task_pump_; + base::WaitableEvent started_event_; + MessageLoop* message_loop_; + + DISALLOW_COPY_AND_ASSIGN(JingleThread); +}; + +} // namespace remoting + +#endif // REMOTING_JINGLE_GLUE_JINGLE_THREAD_H diff --git a/remoting/jingle_glue/jingle_thread_unittest.cc b/remoting/jingle_glue/jingle_thread_unittest.cc new file mode 100644 index 0000000..a29b69c --- /dev/null +++ b/remoting/jingle_glue/jingle_thread_unittest.cc @@ -0,0 +1,27 @@ +// 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 "base/message_loop.h" +#include "remoting/jingle_glue/jingle_thread.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace remoting { + +class MockTask : public Task { + public: + MOCK_METHOD0(Run, void()); +}; + +TEST(JingleThreadTest, PostTask) { + JingleThread thread; + MockTask* task = new MockTask(); + EXPECT_CALL(*task, Run()); + + thread.Start(); + thread.message_loop()->PostTask(FROM_HERE, task); + thread.Stop(); +} + +} // namespace remoting diff --git a/remoting/jingle_glue/mock_objects.h b/remoting/jingle_glue/mock_objects.h new file mode 100644 index 0000000..c1a1e26 --- /dev/null +++ b/remoting/jingle_glue/mock_objects.h @@ -0,0 +1,26 @@ +// 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_JINGLE_GLUE_MOCK_OBJECTS_H_ +#define REMOTING_JINGLE_GLUE_MOCK_OBJECTS_H_ + +#include "remoting/jingle_glue/jingle_channel.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace remoting { + +class MockJingleChannel : public JingleChannel { + public: + MockJingleChannel() {} + + MOCK_METHOD1(Write, void(scoped_refptr<media::DataBuffer> data)); + MOCK_METHOD0(Close, void()); + + private: + DISALLOW_COPY_AND_ASSIGN(MockJingleChannel); +}; + +} // namespace remoting + +#endif // REMOTING_JINGLE_GLUE_MOCK_OBJECTS_H_ diff --git a/remoting/jingle_glue/relay_port_allocator.cc b/remoting/jingle_glue/relay_port_allocator.cc new file mode 100644 index 0000000..8b50dca --- /dev/null +++ b/remoting/jingle_glue/relay_port_allocator.cc @@ -0,0 +1,29 @@ +// 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/jingle_glue/relay_port_allocator.h" + +#include "remoting/jingle_glue/jingle_info_task.h" +#include "talk/xmpp/xmppclient.h" + +namespace remoting { + +void RelayPortAllocator::OnJingleInfo( + const std::string & token, + const std::vector<std::string> & relay_hosts, + const std::vector<talk_base::SocketAddress> & stun_hosts) { + this->SetRelayToken(token); + this->SetStunHosts(stun_hosts); + this->SetRelayHosts(relay_hosts); +} + +void RelayPortAllocator::SetJingleInfo(buzz::XmppClient* client) { + // The JingleInfoTask is freed by the task-runner. + JingleInfoTask* jit = new JingleInfoTask(client); + jit->SignalJingleInfo.connect(this, &RelayPortAllocator::OnJingleInfo); + jit->Start(); + jit->RefreshJingleInfoNow(); +} + +} // namespace remoting diff --git a/remoting/jingle_glue/relay_port_allocator.h b/remoting/jingle_glue/relay_port_allocator.h new file mode 100644 index 0000000..2651d3f3 --- /dev/null +++ b/remoting/jingle_glue/relay_port_allocator.h @@ -0,0 +1,38 @@ +// 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_JINGLE_GLUE_RELAY_PORT_ALLOCATOR_H_ +#define REMOTING_JINGLE_GLUE_RELAY_PORT_ALLOCATOR_H_ + +#include <string> + +#include "talk/base/sigslot.h" +#include "talk/p2p/client/httpportallocator.h" + +namespace buzz { +class XmppClient; +} // namespace buzz + +namespace remoting { + +class RelayPortAllocator: public cricket::HttpPortAllocator, + public sigslot::has_slots<> { + public: + RelayPortAllocator(talk_base::NetworkManager* network_manager, + const std::string& user_agent): + cricket::HttpPortAllocator(network_manager, user_agent) { } + + void OnJingleInfo(const std::string& token, + const std::vector<std::string>& relay_hosts, + const std::vector<talk_base::SocketAddress>& stun_hosts); + + void SetJingleInfo(buzz::XmppClient* client); + + private: + DISALLOW_COPY_AND_ASSIGN(RelayPortAllocator); +}; + +} // namespace remoting + +#endif // REMOTING_JINGLE_GLUE_RELAY_PORT_ALLOCATOR_H_ |