1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "remoting/signaling/xmpp_signal_strategy.h"
7 #include "base/base64.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/run_loop.h"
10 #include "net/socket/socket_test_util.h"
11 #include "net/url_request/url_request_test_util.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
19 class XmppSocketDataProvider
: public net::SocketDataProvider
{
21 net::MockRead
GetNextRead() override
{
22 return net::MockRead(net::ASYNC
, net::ERR_IO_PENDING
);
25 net::MockWriteResult
OnWrite(const std::string
& data
) override
{
26 written_data_
.append(data
);
27 return net::MockWriteResult(net::SYNCHRONOUS
, data
.size());
30 void Reset() override
{}
32 void ReceiveData(const std::string
& text
) {
33 socket()->OnReadComplete(
34 net::MockRead(net::ASYNC
, text
.data(), text
.size()));
38 ReceiveData(std::string());
41 void SimulateNetworkError() {
42 socket()->OnReadComplete(
43 net::MockRead(net::ASYNC
, net::ERR_CONNECTION_RESET
));
46 std::string
GetAndClearWrittenData() {
48 data
.swap(written_data_
);
53 std::string written_data_
;
58 const char kTestUsername
[] = "test_username@example.com";
59 const char kTestAuthToken
[] = "test_auth_token";
61 class XmppSignalStrategyTest
: public testing::Test
,
62 public SignalStrategy::Listener
{
64 XmppSignalStrategyTest() : message_loop_(base::MessageLoop::TYPE_IO
) {}
66 void SetUp() override
{
67 scoped_ptr
<net::TestURLRequestContext
> context(
68 new net::TestURLRequestContext());
69 request_context_getter_
= new net::TestURLRequestContextGetter(
70 message_loop_
.task_runner(), context
.Pass());
72 XmppSignalStrategy::XmppServerConfig config
;
73 config
.host
= "talk.google.com";
75 config
.username
= kTestUsername
;
76 config
.auth_token
= kTestAuthToken
;
77 signal_strategy_
.reset(new XmppSignalStrategy(
78 &client_socket_factory_
, request_context_getter_
, config
));
79 signal_strategy_
->AddListener(this);
82 void TearDown() override
{
83 signal_strategy_
->RemoveListener(this);
84 signal_strategy_
.reset();
85 base::RunLoop().RunUntilIdle();
88 void OnSignalStrategyStateChange(SignalStrategy::State state
) override
{
89 state_history_
.push_back(state
);
92 bool OnSignalStrategyIncomingStanza(const buzz::XmlElement
* stanza
) override
{
93 received_messages_
.push_back(
94 make_scoped_ptr(new buzz::XmlElement(*stanza
)));
98 void Connect(bool success
);
101 base::MessageLoop message_loop_
;
102 scoped_refptr
<net::TestURLRequestContextGetter
> request_context_getter_
;
103 net::MockClientSocketFactory client_socket_factory_
;
104 scoped_ptr
<XmppSocketDataProvider
> socket_data_provider_
;
105 scoped_ptr
<net::SSLSocketDataProvider
> ssl_socket_data_provider_
;
106 scoped_ptr
<XmppSignalStrategy
> signal_strategy_
;
108 std::vector
<SignalStrategy::State
> state_history_
;
109 ScopedVector
<buzz::XmlElement
> received_messages_
;
112 void XmppSignalStrategyTest::Connect(bool success
) {
113 EXPECT_EQ(SignalStrategy::DISCONNECTED
, signal_strategy_
->GetState());
114 state_history_
.clear();
116 socket_data_provider_
.reset(new XmppSocketDataProvider());
117 socket_data_provider_
->set_connect_data(
118 net::MockConnect(net::ASYNC
, net::OK
));
119 client_socket_factory_
.AddSocketDataProvider(socket_data_provider_
.get());
121 ssl_socket_data_provider_
.reset(
122 new net::SSLSocketDataProvider(net::ASYNC
, net::OK
));
123 client_socket_factory_
.AddSSLSocketDataProvider(
124 ssl_socket_data_provider_
.get());
126 signal_strategy_
->Connect();
128 EXPECT_EQ(SignalStrategy::CONNECTING
, signal_strategy_
->GetState());
129 EXPECT_EQ(1U, state_history_
.size());
130 EXPECT_EQ(SignalStrategy::CONNECTING
, state_history_
[0]);
132 // No data written before TLS.
133 EXPECT_EQ("", socket_data_provider_
->GetAndClearWrittenData());
135 base::RunLoop().RunUntilIdle();
137 socket_data_provider_
->ReceiveData(
138 "<stream:stream from=\"google.com\" id=\"DCDDE5171CB2154A\" "
140 "xmlns:stream=\"http://etherx.jabber.org/streams\" "
141 "xmlns=\"jabber:client\">"
143 "<mechanisms xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">"
144 "<mechanism>X-OAUTH2</mechanism>"
145 "<mechanism>X-GOOGLE-TOKEN</mechanism>"
146 "<mechanism>PLAIN</mechanism>"
148 "</stream:features>");
150 base::RunLoop().RunUntilIdle();
153 base::Base64Encode(std::string("\0", 1) + kTestUsername
+
154 std::string("\0", 1) + kTestAuthToken
,
156 // Expect auth message.
158 "<stream:stream to=\"google.com\" version=\"1.0\" "
159 "xmlns=\"jabber:client\" "
160 "xmlns:stream=\"http://etherx.jabber.org/streams\">"
161 "<auth xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" mechanism=\"X-OAUTH2\" "
162 "auth:service=\"oauth2\" auth:allow-generated-jid=\"true\" "
163 "auth:client-uses-full-bind-result=\"true\" "
164 "auth:allow-non-google-login=\"true\" "
165 "xmlns:auth=\"http://www.google.com/talk/protocol/auth\">" + cookie
+
166 "</auth>", socket_data_provider_
->GetAndClearWrittenData());
169 socket_data_provider_
->ReceiveData(
170 "<failure xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">"
171 "<not-authorized/></failure>");
172 EXPECT_EQ(2U, state_history_
.size());
173 EXPECT_EQ(SignalStrategy::DISCONNECTED
, state_history_
[1]);
174 EXPECT_EQ(SignalStrategy::AUTHENTICATION_FAILED
,
175 signal_strategy_
->GetError());
179 socket_data_provider_
->ReceiveData(
180 "<success xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\"/>");
182 base::RunLoop().RunUntilIdle();
185 "<stream:stream to=\"google.com\" version=\"1.0\" "
186 "xmlns=\"jabber:client\" "
187 "xmlns:stream=\"http://etherx.jabber.org/streams\">"
188 "<iq type=\"set\" id=\"0\">"
189 "<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\">"
190 "<resource>chromoting</resource>"
193 "<iq type=\"set\" id=\"1\">"
194 "<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>"
196 socket_data_provider_
->GetAndClearWrittenData());
197 socket_data_provider_
->ReceiveData(
198 "<stream:stream from=\"google.com\" id=\"104FA10576E2AA80\" "
200 "xmlns:stream=\"http://etherx.jabber.org/streams\" "
201 "xmlns=\"jabber:client\">"
203 "<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/>"
204 "<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>"
206 "<iq id=\"0\" type=\"result\">"
207 "<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\">"
208 "<jid>" + std::string(kTestUsername
) + "/chromoting52B4920E</jid>"
211 "<iq type=\"result\" id=\"1\"/>");
213 EXPECT_EQ(2U, state_history_
.size());
214 EXPECT_EQ(SignalStrategy::CONNECTED
, state_history_
[1]);
217 TEST_F(XmppSignalStrategyTest
, SendAndReceive
) {
220 EXPECT_TRUE(signal_strategy_
->SendStanza(make_scoped_ptr(
221 new buzz::XmlElement(buzz::QName(std::string(), "hello")))));
222 EXPECT_EQ("<hello/>", socket_data_provider_
->GetAndClearWrittenData());
224 socket_data_provider_
->ReceiveData("<hi xmlns=\"hello\"/>");
225 EXPECT_EQ(1U, received_messages_
.size());
226 EXPECT_EQ("<hi xmlns=\"hello\"/>", received_messages_
[0]->Str());
229 TEST_F(XmppSignalStrategyTest
, AuthError
) {
233 TEST_F(XmppSignalStrategyTest
, ConnectionClosed
) {
236 socket_data_provider_
->Close();
238 EXPECT_EQ(3U, state_history_
.size());
239 EXPECT_EQ(SignalStrategy::DISCONNECTED
, state_history_
[2]);
240 EXPECT_EQ(SignalStrategy::DISCONNECTED
, signal_strategy_
->GetState());
241 EXPECT_EQ(SignalStrategy::OK
, signal_strategy_
->GetError());
243 // Can't send messages anymore.
244 EXPECT_FALSE(signal_strategy_
->SendStanza(make_scoped_ptr(
245 new buzz::XmlElement(buzz::QName(std::string(), "hello")))));
247 // Try connecting again.
251 TEST_F(XmppSignalStrategyTest
, NetworkError
) {
254 socket_data_provider_
->SimulateNetworkError();
256 EXPECT_EQ(3U, state_history_
.size());
257 EXPECT_EQ(SignalStrategy::DISCONNECTED
, state_history_
[2]);
258 EXPECT_EQ(SignalStrategy::NETWORK_ERROR
, signal_strategy_
->GetError());
260 // Can't send messages anymore.
261 EXPECT_FALSE(signal_strategy_
->SendStanza(make_scoped_ptr(
262 new buzz::XmlElement(buzz::QName(std::string(), "hello")))));
264 // Try connecting again.
268 } // namespace remoting