OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
| 5 #include <sched.h> |
| 6 #include <unistd.h> |
| 7 |
5 #include "base/memory/ref_counted.h" | 8 #include "base/memory/ref_counted.h" |
| 9 #include "base/run_loop.h" |
6 #include "base/strings/stringprintf.h" | 10 #include "base/strings/stringprintf.h" |
| 11 #include "base/synchronization/lock.h" |
7 #include "extensions/browser/api/dns/host_resolver_wrapper.h" | 12 #include "extensions/browser/api/dns/host_resolver_wrapper.h" |
8 #include "extensions/browser/api/dns/mock_host_resolver_creator.h" | 13 #include "extensions/browser/api/dns/mock_host_resolver_creator.h" |
9 #include "extensions/browser/api/sockets_tcp/sockets_tcp_api.h" | 14 #include "extensions/browser/api/sockets_tcp/sockets_tcp_api.h" |
10 #include "extensions/browser/api_test_utils.h" | 15 #include "extensions/browser/api_test_utils.h" |
| 16 #include "extensions/browser/event_router.h" |
| 17 #include "extensions/browser/event_router_factory.h" |
| 18 #include "extensions/browser/extension_function_dispatcher.h" |
| 19 #include "extensions/browser/extension_prefs.h" |
| 20 #include "extensions/common/api/sockets/sockets_manifest_data.h" |
11 #include "extensions/common/extension.h" | 21 #include "extensions/common/extension.h" |
| 22 #include "extensions/common/extension_builder.h" |
| 23 #include "extensions/common/manifest_constants.h" |
12 #include "extensions/common/test_util.h" | 24 #include "extensions/common/test_util.h" |
| 25 #include "extensions/common/value_builder.h" |
13 #include "extensions/shell/test/shell_apitest.h" | 26 #include "extensions/shell/test/shell_apitest.h" |
14 #include "extensions/shell/test/shell_test.h" | 27 #include "extensions/shell/test/shell_test.h" |
15 #include "extensions/test/extension_test_message_listener.h" | 28 #include "extensions/test/extension_test_message_listener.h" |
16 #include "extensions/test/result_catcher.h" | 29 #include "extensions/test/result_catcher.h" |
| 30 #include "net/base/io_buffer.h" |
| 31 #include "net/base/net_errors.h" |
17 #include "net/dns/mock_host_resolver.h" | 32 #include "net/dns/mock_host_resolver.h" |
| 33 #include "net/socket/tcp_server_socket.h" |
18 #include "net/test/spawned_test_server/spawned_test_server.h" | 34 #include "net/test/spawned_test_server/spawned_test_server.h" |
| 35 #include "testing/gmock/include/gmock/gmock.h" |
19 | 36 |
20 namespace extensions { | 37 namespace extensions { |
| 38 using ::testing::_; |
21 | 39 |
22 const std::string kHostname = "127.0.0.1"; | 40 const std::string kHostname = "127.0.0.1"; |
23 | 41 |
| 42 // Runs a single-connection TCP echo server on a separate thread. |
| 43 class LocalServer : public base::RefCountedThreadSafe<LocalServer> { |
| 44 public: |
| 45 LocalServer() |
| 46 : echo_socket_(nullptr, net::NetLog::Source()), |
| 47 thread_("localserver"), |
| 48 active_(false), |
| 49 issued_write_count_(0), |
| 50 finished_write_count_(0), |
| 51 port_(-1), |
| 52 sock_(nullptr), |
| 53 state_cvar_(&state_lock_) {} |
| 54 |
| 55 void Start() { |
| 56 // Should run in an IO thread (or at least one with a MessageLoopForIO) |
| 57 base::Thread::Options thread_opts(base::MessageLoop::TYPE_IO, 0); |
| 58 thread_.StartWithOptions(thread_opts); |
| 59 thread_.WaitUntilThreadStarted(); |
| 60 thread_.message_loop()->PostTask( |
| 61 FROM_HERE, base::Bind(&LocalServer::IOStart, base::Unretained(this))); |
| 62 } |
| 63 |
| 64 void Close() { |
| 65 thread_.message_loop()->PostTask(FROM_HERE, |
| 66 base::Bind(&LocalServer::DoClose, this)); |
| 67 } |
| 68 |
| 69 // This is -1 until IOStart() has run on this LocalServer's IO thread |
| 70 int port() { |
| 71 int ret = -1; |
| 72 do { |
| 73 { |
| 74 base::AutoLock l(state_lock_); |
| 75 ret = port_; |
| 76 } |
| 77 if (port_ < 0) { |
| 78 LOG(INFO) << "port(): waiting for thread to finish doing whatever it's " |
| 79 "doing."; |
| 80 usleep(25000); |
| 81 } |
| 82 } while (ret < 0); |
| 83 return ret; |
| 84 } |
| 85 |
| 86 // Whether Accept() completed, making sock_ valid. |
| 87 bool accepted() { |
| 88 base::AutoLock l(state_lock_); |
| 89 return active_; |
| 90 } |
| 91 |
| 92 // Returns the total number of issued writes. If this call returns N, then |
| 93 // you can verify that the underlying write finished with |
| 94 // wait_for_n_finished_writes(N). |
| 95 int Write(const char* message) { return Write(std::string(message)); } |
| 96 |
| 97 int Write(const std::string& message) { |
| 98 thread_.message_loop()->PostTask( |
| 99 FROM_HERE, |
| 100 base::Bind(&LocalServer::DoWrite, base::Unretained(this), message)); |
| 101 return ++issued_write_count_; |
| 102 } |
| 103 |
| 104 int issued_write_count() { |
| 105 base::AutoLock l(state_lock_); |
| 106 return issued_write_count_; |
| 107 } |
| 108 |
| 109 int finished_write_count() { |
| 110 base::AutoLock l(state_lock_); |
| 111 return finished_write_count_; |
| 112 } |
| 113 |
| 114 void WaitForNFinishedWrites(int nr) { |
| 115 base::AutoLock l(state_lock_); |
| 116 while (finished_write_count_ < nr) { |
| 117 state_cvar_.Wait(); |
| 118 } |
| 119 } |
| 120 |
| 121 private: |
| 122 net::TCPServerSocket echo_socket_; |
| 123 base::Thread thread_; // Our IO thread. |
| 124 bool active_; |
| 125 int issued_write_count_; // How many calls to Write() have been made? |
| 126 int finished_write_count_; // How many calls to FinishWrite() have been made? |
| 127 int port_; |
| 128 scoped_ptr<net::StreamSocket> sock_; // not for use on the primary thread! |
| 129 base::Lock state_lock_; |
| 130 base::ConditionVariable state_cvar_; |
| 131 |
| 132 friend class base::RefCountedThreadSafe<LocalServer>; |
| 133 virtual ~LocalServer() { |
| 134 bool active; |
| 135 { |
| 136 base::AutoLock l(state_lock_); |
| 137 active = active_; |
| 138 } |
| 139 DCHECK(!active); |
| 140 } |
| 141 |
| 142 void FinishWrite(int result) { |
| 143 base::AutoLock l(state_lock_); |
| 144 ASSERT_GT(result, 0); |
| 145 LOG(INFO) << "FinishWrite(result=" << result << ")"; |
| 146 finished_write_count_++; |
| 147 state_cvar_.Broadcast(); |
| 148 } |
| 149 |
| 150 void DoWrite(const std::string& message) { |
| 151 { |
| 152 base::AutoLock l(state_lock_); |
| 153 DCHECK(active_); |
| 154 issued_write_count_++; |
| 155 } |
| 156 scoped_refptr<net::StringIOBuffer> buf(new net::StringIOBuffer(message)); |
| 157 LOG(INFO) << "Write(" << message << ")"; |
| 158 int result = sock_->Write( |
| 159 buf.get(), buf->size(), |
| 160 base::Bind(&LocalServer::FinishWrite, base::Unretained(this))); |
| 161 if (result != net::ERR_IO_PENDING) { |
| 162 FinishWrite(result); |
| 163 } |
| 164 } |
| 165 |
| 166 void IOStart() { |
| 167 base::AutoLock l(state_lock_); |
| 168 net::IPAddressNumber localhost; |
| 169 net::IPEndPoint local; |
| 170 net::ParseIPLiteralToNumber("127.0.0.1", &localhost); |
| 171 // setup echo_socket_. |
| 172 echo_socket_.Listen(net::IPEndPoint(localhost, 0), 5); |
| 173 echo_socket_.GetLocalAddress(&local); |
| 174 port_ = local.port(); |
| 175 echo_socket_.Accept( |
| 176 &sock_, base::Bind(&LocalServer::AcceptedSock, base::Unretained(this))); |
| 177 } |
| 178 |
| 179 void AcceptedSock(int result) { |
| 180 base::AutoLock l(state_lock_); |
| 181 ASSERT_EQ(net::OK, result); |
| 182 active_ = true; |
| 183 } |
| 184 |
| 185 void DoClose() { |
| 186 base::AutoLock l(state_lock_); |
| 187 // Repeatedly calling this is harmless. |
| 188 sock_->Disconnect(); |
| 189 active_ = false; |
| 190 } |
| 191 }; |
| 192 |
| 193 class MockEventRouter : public EventRouter { |
| 194 public: |
| 195 MockEventRouter(content::BrowserContext* ctx, ExtensionPrefs* prefs) |
| 196 : EventRouter(ctx, prefs), do_dispatch_(true), call_count_(0) {} |
| 197 |
| 198 MOCK_METHOD3(DoDispatchEventToExtension, |
| 199 void(const std::string&, const std::string&, Event*)); |
| 200 |
| 201 void set_dispatch(bool v) { do_dispatch_ = v; } |
| 202 int call_count() { return call_count_; } |
| 203 |
| 204 // Trampoline method to work around GMOCK/scoped_ptr problems. |
| 205 void DispatchEventToExtension(const std::string& str, |
| 206 scoped_ptr<Event> evt) override { |
| 207 LOG(INFO) << "MockEventRouter: Got event: " << evt->event_name; |
| 208 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 209 call_count_++; |
| 210 DoDispatchEventToExtension(str, evt->event_name, evt.get()); |
| 211 if (do_dispatch_) { |
| 212 EventRouter::DispatchEventToExtension(str, |
| 213 scoped_ptr<Event>(evt.release())); |
| 214 } |
| 215 } |
| 216 |
| 217 private: |
| 218 bool do_dispatch_; |
| 219 int call_count_; |
| 220 }; |
| 221 |
24 class SocketsTcpApiTest : public ShellApiTest { | 222 class SocketsTcpApiTest : public ShellApiTest { |
25 public: | 223 public: |
26 SocketsTcpApiTest() | 224 SocketsTcpApiTest() |
27 : resolver_event_(true, false), | 225 : resolver_event_(true, false), |
28 resolver_creator_(new MockHostResolverCreator()) {} | 226 resolver_creator_(new MockHostResolverCreator()) {} |
29 | 227 |
| 228 void EnableMockRouter() { |
| 229 // Setup a Mock EventRouter to catch output from the |
| 230 // TCPSocketEventDispatcher |
| 231 EventRouterFactory::GetInstance()->SetTestingFactoryAndUse( |
| 232 browser_context(), &BuildMockRouter); |
| 233 } |
| 234 |
30 void SetUpOnMainThread() override { | 235 void SetUpOnMainThread() override { |
31 ShellApiTest::SetUpOnMainThread(); | 236 ShellApiTest::SetUpOnMainThread(); |
32 | 237 |
33 HostResolverWrapper::GetInstance()->SetHostResolverForTesting( | 238 HostResolverWrapper::GetInstance()->SetHostResolverForTesting( |
34 resolver_creator_->CreateMockHostResolver()); | 239 resolver_creator_->CreateMockHostResolver()); |
35 } | 240 } |
36 | 241 |
37 void TearDownOnMainThread() override { | 242 void TearDownOnMainThread() override { |
| 243 LOG(INFO) << "TearDown starting"; |
38 HostResolverWrapper::GetInstance()->SetHostResolverForTesting(NULL); | 244 HostResolverWrapper::GetInstance()->SetHostResolverForTesting(NULL); |
39 resolver_creator_->DeleteMockHostResolver(); | 245 resolver_creator_->DeleteMockHostResolver(); |
40 | 246 |
| 247 LOG(INFO) << "super TearDown starting"; |
41 ShellApiTest::TearDownOnMainThread(); | 248 ShellApiTest::TearDownOnMainThread(); |
| 249 LOG(INFO) << "TearDown done"; |
| 250 } |
| 251 |
| 252 protected: |
| 253 // Makes the extension if we haven't built it yet. |
| 254 scoped_refptr<Extension> extension() { |
| 255 if (!extension_.get()) { |
| 256 DictionaryBuilder app; |
| 257 app.Set("background", |
| 258 DictionaryBuilder().Set("scripts", |
| 259 ListBuilder().Append("background.J's"))); |
| 260 manifest_builder_.Set("name", "Test") |
| 261 .Set("version", "1.0") |
| 262 .Set(manifest_keys::kApp, app); |
| 263 extension_ = ExtensionBuilder().SetManifest(manifest_builder_).Build(); |
| 264 } |
| 265 return extension_; |
| 266 } |
| 267 |
| 268 static scoped_ptr<KeyedService> BuildMockRouter( |
| 269 content::BrowserContext* ctx) { |
| 270 return scoped_ptr<KeyedService>( |
| 271 new MockEventRouter(ctx, ExtensionPrefs::Get(ctx))); |
| 272 } |
| 273 |
| 274 MockEventRouter& mock_router() { |
| 275 return *static_cast<MockEventRouter*>(EventRouter::Get(browser_context())); |
| 276 } |
| 277 |
| 278 template <class F> |
| 279 scoped_ptr<base::Value> CallFunction(std::string args) { |
| 280 scoped_refptr<F> function = new F(); |
| 281 function->set_extension(extension()); |
| 282 return scoped_ptr<base::Value>( |
| 283 api_test_utils::RunFunctionAndReturnSingleResult(function.get(), args, |
| 284 browser_context())); |
| 285 } |
| 286 |
| 287 // Adds parts to the currently-building manifest. Call |extension()| to |
| 288 // create the extension with the manifest. |
| 289 void SetManifestPermissions(const std::string& perms) { |
| 290 base::string16 error; |
| 291 scoped_ptr<base::DictionaryValue> permissionsDict = |
| 292 api_test_utils::ParseDictionary(perms); |
| 293 // Validate the permissions. |
| 294 DCHECK(extension_.get() == nullptr); |
| 295 if (SocketsManifestData::FromValue(*permissionsDict.get(), &error).get()) { |
| 296 DictionaryBuilder dbuilder(*permissionsDict.get()); |
| 297 manifest_builder_.Set(manifest_keys::kSockets, dbuilder); |
| 298 } else { |
| 299 LOG(ERROR) << "Failed to setup socket manifest permissions: " << error |
| 300 << " for value: " << perms; |
| 301 } |
42 } | 302 } |
43 | 303 |
44 private: | 304 private: |
45 base::WaitableEvent resolver_event_; | 305 base::WaitableEvent resolver_event_; |
| 306 DictionaryBuilder manifest_builder_; |
| 307 scoped_refptr<Extension> extension_; |
46 | 308 |
47 // The MockHostResolver asserts that it's used on the same thread on which | 309 // The MockHostResolver asserts that it's used on the same thread on which |
48 // it's created, which is actually a stronger rule than its real counterpart. | 310 // it's created, which is actually a stronger rule than its real counterpart. |
49 // But that's fine; it's good practice. | 311 // But that's fine; it's good practice. |
50 scoped_refptr<MockHostResolverCreator> resolver_creator_; | 312 scoped_refptr<MockHostResolverCreator> resolver_creator_; |
51 }; | 313 }; |
52 | 314 |
53 IN_PROC_BROWSER_TEST_F(SocketsTcpApiTest, SocketsTcpCreateGood) { | 315 IN_PROC_BROWSER_TEST_F(SocketsTcpApiTest, SocketsTcpCreateIsGood) { |
54 scoped_refptr<api::SocketsTcpCreateFunction> socket_create_function( | |
55 new api::SocketsTcpCreateFunction()); | |
56 scoped_refptr<Extension> empty_extension = test_util::CreateEmptyExtension(); | |
57 | |
58 socket_create_function->set_extension(empty_extension.get()); | |
59 socket_create_function->set_has_callback(true); | |
60 | |
61 scoped_ptr<base::Value> result( | 316 scoped_ptr<base::Value> result( |
62 api_test_utils::RunFunctionAndReturnSingleResult( | 317 CallFunction<api::SocketsTcpCreateFunction>("[]")); |
63 socket_create_function.get(), "[]", browser_context())); | |
64 | 318 |
65 ASSERT_EQ(base::Value::TYPE_DICTIONARY, result->GetType()); | 319 ASSERT_EQ(base::Value::TYPE_DICTIONARY, result->GetType()); |
66 base::DictionaryValue* value = | 320 base::DictionaryValue* value = |
67 static_cast<base::DictionaryValue*>(result.get()); | 321 static_cast<base::DictionaryValue*>(result.get()); |
68 int socketId = -1; | 322 int socketId = -1; |
69 EXPECT_TRUE(value->GetInteger("socketId", &socketId)); | 323 EXPECT_TRUE(value->GetInteger("socketId", &socketId)); |
70 ASSERT_TRUE(socketId > 0); | 324 ASSERT_GT(socketId, 0); |
71 } | 325 } |
72 | 326 |
73 IN_PROC_BROWSER_TEST_F(SocketsTcpApiTest, SocketTcpExtension) { | 327 IN_PROC_BROWSER_TEST_F(SocketsTcpApiTest, SocketTcpExtension) { |
74 scoped_ptr<net::SpawnedTestServer> test_server(new net::SpawnedTestServer( | 328 scoped_ptr<net::SpawnedTestServer> test_server(new net::SpawnedTestServer( |
75 net::SpawnedTestServer::TYPE_TCP_ECHO, net::SpawnedTestServer::kLocalhost, | 329 net::SpawnedTestServer::TYPE_TCP_ECHO, net::SpawnedTestServer::kLocalhost, |
76 base::FilePath(FILE_PATH_LITERAL("net/data")))); | 330 base::FilePath(FILE_PATH_LITERAL("net/data")))); |
77 EXPECT_TRUE(test_server->Start()); | 331 EXPECT_TRUE(test_server->Start()); |
78 | 332 |
79 net::HostPortPair host_port_pair = test_server->host_port_pair(); | 333 net::HostPortPair host_port_pair = test_server->host_port_pair(); |
80 int port = host_port_pair.port(); | 334 int port = host_port_pair.port(); |
81 ASSERT_TRUE(port > 0); | 335 ASSERT_GT(port, 0); |
82 | 336 |
83 // Test that connect() is properly resolving hostnames. | 337 // Test that connect() is properly resolving hostnames. |
84 host_port_pair.set_host("lOcAlHoSt"); | 338 host_port_pair.set_host("lOcAlHoSt"); |
85 | 339 |
86 ResultCatcher catcher; | 340 ResultCatcher catcher; |
87 catcher.RestrictToBrowserContext(browser_context()); | 341 catcher.RestrictToBrowserContext(browser_context()); |
88 | 342 |
89 ExtensionTestMessageListener listener("info_please", true); | 343 ExtensionTestMessageListener listener("info_please", true); |
90 | 344 |
91 ASSERT_TRUE(LoadApp("sockets_tcp/api")); | 345 ASSERT_TRUE(LoadApp("sockets_tcp/api")); |
(...skipping 21 matching lines...) Expand all Loading... |
113 ExtensionTestMessageListener listener("info_please", true); | 367 ExtensionTestMessageListener listener("info_please", true); |
114 | 368 |
115 ASSERT_TRUE(LoadApp("sockets_tcp/api")); | 369 ASSERT_TRUE(LoadApp("sockets_tcp/api")); |
116 EXPECT_TRUE(listener.WaitUntilSatisfied()); | 370 EXPECT_TRUE(listener.WaitUntilSatisfied()); |
117 listener.Reply(base::StringPrintf( | 371 listener.Reply(base::StringPrintf( |
118 "https:%s:%d", https_host_port_pair.host().c_str(), https_port)); | 372 "https:%s:%d", https_host_port_pair.host().c_str(), https_port)); |
119 | 373 |
120 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); | 374 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); |
121 } | 375 } |
122 | 376 |
| 377 IN_PROC_BROWSER_TEST_F(SocketsTcpApiTest, SocketsTcpPauseIsGood) { |
| 378 EnableMockRouter(); |
| 379 |
| 380 scoped_refptr<LocalServer> server(new LocalServer); |
| 381 server->Start(); |
| 382 int port = server->port(); |
| 383 |
| 384 LOG(INFO) << "Port " << port; |
| 385 SetManifestPermissions(base::StringPrintf( |
| 386 "{ \"tcp\": { \"connect\": \"localhost:%d\" } }", port)); |
| 387 |
| 388 scoped_ptr<base::Value> create( |
| 389 CallFunction<api::SocketsTcpCreateFunction>("[{\"persistent\":true}]")); |
| 390 int sockfd; |
| 391 base::DictionaryValue* create_val = |
| 392 static_cast<base::DictionaryValue*>(create.get()); |
| 393 EXPECT_TRUE(create_val->GetInteger("socketId", &sockfd)); |
| 394 ASSERT_GT(sockfd, 0); |
| 395 |
| 396 scoped_ptr<base::Value> conn_result = |
| 397 CallFunction<api::SocketsTcpConnectFunction>( |
| 398 base::StringPrintf("[%d, \"localhost\", %d]", sockfd, port)); |
| 399 LOG(INFO) << "Finished running connect()"; |
| 400 |
| 401 // Require that we receive two OnReceive events, with a "MARK" event in the |
| 402 // middle. So we can make sure that we don't receive an OnReceive while |
| 403 // paused. |
| 404 { |
| 405 ::testing::InSequence seq; |
| 406 EXPECT_CALL(mock_router(), |
| 407 DoDispatchEventToExtension(_, "sockets.tcp.onReceive", _)) |
| 408 .Times(1); |
| 409 EXPECT_CALL(mock_router(), DoDispatchEventToExtension(_, "MARK", _)) |
| 410 .Times(1); |
| 411 EXPECT_CALL(mock_router(), |
| 412 DoDispatchEventToExtension(_, "sockets.tcp.onReceive", _)) |
| 413 .Times(1) |
| 414 .RetiresOnSaturation(); |
| 415 } |
| 416 |
| 417 // Write the first message, and wait for it to have been issued. |
| 418 server->Write("first message\n"); |
| 419 server->WaitForNFinishedWrites(1); |
| 420 // Make sure that the onReceive comes before the MARK gets sent over. |
| 421 while (mock_router().call_count() < 1) { |
| 422 LOG(INFO) << "RunLoop call {"; |
| 423 base::RunLoop().RunUntilIdle(); |
| 424 LOG(INFO) << "} RunLoop call finished."; |
| 425 } |
| 426 |
| 427 CallFunction<api::SocketsTcpSetPausedFunction>( |
| 428 base::StringPrintf("[%d, true]", sockfd)); |
| 429 |
| 430 server->Write("second message\n"); |
| 431 server->WaitForNFinishedWrites(2); |
| 432 // This will result in a message before the Write() above, as it's paused. |
| 433 mock_router().DoDispatchEventToExtension("", "MARK", nullptr); |
| 434 |
| 435 // Let the Write() get delivered. |
| 436 CallFunction<api::SocketsTcpSetPausedFunction>( |
| 437 base::StringPrintf("[%d, false]", sockfd)); |
| 438 |
| 439 while (mock_router().call_count() < 2) { |
| 440 // Let event queues drain. |
| 441 base::RunLoop().RunUntilIdle(); |
| 442 } |
| 443 |
| 444 server->Close(); |
| 445 } |
| 446 |
123 } // namespace extensions | 447 } // namespace extensions |
OLD | NEW |