| OLD | NEW |
| (Empty) | |
| 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. |
| 4 |
| 5 #include "remoting/signaling/xmpp_signal_strategy.h" |
| 6 |
| 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" |
| 14 |
| 15 namespace remoting { |
| 16 |
| 17 namespace { |
| 18 |
| 19 class XmppSocketDataProvider: public net::SocketDataProvider { |
| 20 public: |
| 21 net::MockRead GetNextRead() override { |
| 22 return net::MockRead(net::ASYNC, net::ERR_IO_PENDING); |
| 23 } |
| 24 |
| 25 net::MockWriteResult OnWrite(const std::string& data) override { |
| 26 written_data_.append(data); |
| 27 return net::MockWriteResult(net::SYNCHRONOUS, data.size()); |
| 28 } |
| 29 |
| 30 void Reset() override {} |
| 31 |
| 32 void ReceiveData(const std::string& text) { |
| 33 socket()->OnReadComplete( |
| 34 net::MockRead(net::ASYNC, text.data(), text.size())); |
| 35 } |
| 36 |
| 37 void Close() { |
| 38 ReceiveData(std::string()); |
| 39 } |
| 40 |
| 41 void SimulateNetworkError() { |
| 42 socket()->OnReadComplete( |
| 43 net::MockRead(net::ASYNC, net::ERR_CONNECTION_RESET)); |
| 44 } |
| 45 |
| 46 std::string GetAndClearWrittenData() { |
| 47 std::string data; |
| 48 data.swap(written_data_); |
| 49 return data; |
| 50 } |
| 51 |
| 52 private: |
| 53 std::string written_data_; |
| 54 }; |
| 55 |
| 56 } // namespace |
| 57 |
| 58 const char kTestUsername[] = "test_username@example.com"; |
| 59 const char kTestAuthToken[] = "test_auth_token"; |
| 60 |
| 61 class XmppSignalStrategyTest : public testing::Test, |
| 62 public SignalStrategy::Listener { |
| 63 public: |
| 64 XmppSignalStrategyTest() : message_loop_(base::MessageLoop::TYPE_IO) {} |
| 65 |
| 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()); |
| 71 |
| 72 XmppSignalStrategy::XmppServerConfig config; |
| 73 config.host = "talk.google.com"; |
| 74 config.port = 443; |
| 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); |
| 80 } |
| 81 |
| 82 void TearDown() override { |
| 83 signal_strategy_->RemoveListener(this); |
| 84 signal_strategy_.reset(); |
| 85 base::RunLoop().RunUntilIdle(); |
| 86 } |
| 87 |
| 88 void OnSignalStrategyStateChange(SignalStrategy::State state) override { |
| 89 state_history_.push_back(state); |
| 90 } |
| 91 |
| 92 bool OnSignalStrategyIncomingStanza(const buzz::XmlElement* stanza) override { |
| 93 received_messages_.push_back( |
| 94 make_scoped_ptr(new buzz::XmlElement(*stanza))); |
| 95 return true; |
| 96 } |
| 97 |
| 98 void Connect(bool success); |
| 99 |
| 100 protected: |
| 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_; |
| 107 |
| 108 std::vector<SignalStrategy::State> state_history_; |
| 109 ScopedVector<buzz::XmlElement> received_messages_; |
| 110 }; |
| 111 |
| 112 void XmppSignalStrategyTest::Connect(bool success) { |
| 113 EXPECT_EQ(SignalStrategy::DISCONNECTED, signal_strategy_->GetState()); |
| 114 state_history_.clear(); |
| 115 |
| 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()); |
| 120 |
| 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()); |
| 125 |
| 126 signal_strategy_->Connect(); |
| 127 |
| 128 EXPECT_EQ(SignalStrategy::CONNECTING, signal_strategy_->GetState()); |
| 129 EXPECT_EQ(1U, state_history_.size()); |
| 130 EXPECT_EQ(SignalStrategy::CONNECTING, state_history_[0]); |
| 131 |
| 132 // No data written before TLS. |
| 133 EXPECT_EQ("", socket_data_provider_->GetAndClearWrittenData()); |
| 134 |
| 135 base::RunLoop().RunUntilIdle(); |
| 136 |
| 137 socket_data_provider_->ReceiveData( |
| 138 "<stream:stream from=\"google.com\" id=\"DCDDE5171CB2154A\" " |
| 139 "version=\"1.0\" " |
| 140 "xmlns:stream=\"http://etherx.jabber.org/streams\" " |
| 141 "xmlns=\"jabber:client\">" |
| 142 "<stream:features>" |
| 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>" |
| 147 "</mechanisms>" |
| 148 "</stream:features>"); |
| 149 |
| 150 base::RunLoop().RunUntilIdle(); |
| 151 |
| 152 std::string cookie; |
| 153 base::Base64Encode(std::string("\0", 1) + kTestUsername + |
| 154 std::string("\0", 1) + kTestAuthToken, |
| 155 &cookie); |
| 156 // Expect auth message. |
| 157 EXPECT_EQ( |
| 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()); |
| 167 |
| 168 if (!success) { |
| 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()); |
| 176 return; |
| 177 } |
| 178 |
| 179 socket_data_provider_->ReceiveData( |
| 180 "<success xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\"/>"); |
| 181 |
| 182 base::RunLoop().RunUntilIdle(); |
| 183 |
| 184 EXPECT_EQ( |
| 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>" |
| 191 "</bind>" |
| 192 "</iq>" |
| 193 "<iq type=\"set\" id=\"1\">" |
| 194 "<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>" |
| 195 "</iq>", |
| 196 socket_data_provider_->GetAndClearWrittenData()); |
| 197 socket_data_provider_->ReceiveData( |
| 198 "<stream:stream from=\"google.com\" id=\"104FA10576E2AA80\" " |
| 199 "version=\"1.0\" " |
| 200 "xmlns:stream=\"http://etherx.jabber.org/streams\" " |
| 201 "xmlns=\"jabber:client\">" |
| 202 "<stream:features>" |
| 203 "<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/>" |
| 204 "<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>" |
| 205 "</stream:features>" |
| 206 "<iq id=\"0\" type=\"result\">" |
| 207 "<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\">" |
| 208 "<jid>" + std::string(kTestUsername) + "/chromoting52B4920E</jid>" |
| 209 "</bind>" |
| 210 "</iq>" |
| 211 "<iq type=\"result\" id=\"1\"/>"); |
| 212 |
| 213 EXPECT_EQ(2U, state_history_.size()); |
| 214 EXPECT_EQ(SignalStrategy::CONNECTED, state_history_[1]); |
| 215 } |
| 216 |
| 217 TEST_F(XmppSignalStrategyTest, SendAndReceive) { |
| 218 Connect(true); |
| 219 |
| 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()); |
| 223 |
| 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()); |
| 227 } |
| 228 |
| 229 TEST_F(XmppSignalStrategyTest, AuthError) { |
| 230 Connect(false); |
| 231 } |
| 232 |
| 233 TEST_F(XmppSignalStrategyTest, ConnectionClosed) { |
| 234 Connect(true); |
| 235 |
| 236 socket_data_provider_->Close(); |
| 237 |
| 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()); |
| 242 |
| 243 // Can't send messages anymore. |
| 244 EXPECT_FALSE(signal_strategy_->SendStanza(make_scoped_ptr( |
| 245 new buzz::XmlElement(buzz::QName(std::string(), "hello"))))); |
| 246 |
| 247 // Try connecting again. |
| 248 Connect(true); |
| 249 } |
| 250 |
| 251 TEST_F(XmppSignalStrategyTest, NetworkError) { |
| 252 Connect(true); |
| 253 |
| 254 socket_data_provider_->SimulateNetworkError(); |
| 255 |
| 256 EXPECT_EQ(3U, state_history_.size()); |
| 257 EXPECT_EQ(SignalStrategy::DISCONNECTED, state_history_[2]); |
| 258 EXPECT_EQ(SignalStrategy::NETWORK_ERROR, signal_strategy_->GetError()); |
| 259 |
| 260 // Can't send messages anymore. |
| 261 EXPECT_FALSE(signal_strategy_->SendStanza(make_scoped_ptr( |
| 262 new buzz::XmlElement(buzz::QName(std::string(), "hello"))))); |
| 263 |
| 264 // Try connecting again. |
| 265 Connect(true); |
| 266 } |
| 267 |
| 268 } // namespace remoting |
| OLD | NEW |