devtools_remote_listen_socket_unittest.cc revision dc0f95d653279beabeb9817299e2902918ba123e
1// Copyright (c) 2010 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 "chrome/browser/debugger/devtools_remote_listen_socket_unittest.h" 6 7#include <fcntl.h> 8#if defined(OS_POSIX) 9#include <netinet/in.h> 10#endif 11 12#include "base/eintr_wrapper.h" 13#include "base/test/test_timeouts.h" 14#include "base/threading/platform_thread.h" 15#include "net/base/net_util.h" 16#include "testing/platform_test.h" 17 18const int DevToolsRemoteListenSocketTester::kTestPort = 9999; 19 20static const int kReadBufSize = 1024; 21static const char* kChromeDevToolsHandshake = "ChromeDevToolsHandshake\r\n"; 22static const char* kSimpleMessagePart1 = 23 "Tool:V8Debugger\r\n" 24 "Destination:2\r"; 25static const char* kSimpleMessagePart2 = 26 "\n" 27 "Content-Length:0\r\n" 28 "\r\n"; 29static const char* kTwoMessages = 30 "Tool:DevToolsService\r\n" 31 "Content-Length:300\r\n" 32 "\r\n" 33 "00000000000000000000000000000000000000000000000000" 34 "00000000000000000000000000000000000000000000000000" 35 "00000000000000000000000000000000000000000000000000" 36 "00000000000000000000000000000000000000000000000000" 37 "00000000000000000000000000000000000000000000000000" 38 "00000000000000000000000000000000000000000000000000" 39 "Tool:V8Debugger\r\n" 40 "Destination:1\r\n" 41 "Content-Length:0\r\n" 42 "\r\n"; 43 44static const int kMaxQueueSize = 20; 45static const char* kLoopback = "127.0.0.1"; 46#if defined(OS_POSIX) 47static const char* kSemaphoreName = "chromium.listen_socket"; 48#endif 49 50 51ListenSocketTestAction::ListenSocketTestAction() : action_(ACTION_NONE) {} 52 53ListenSocketTestAction::ListenSocketTestAction(ActionType action) 54 : action_(action) {} 55 56ListenSocketTestAction::ListenSocketTestAction(ActionType action, 57 std::string data) 58 : action_(action), 59 data_(data) {} 60 61ListenSocketTestAction::ListenSocketTestAction( 62 ActionType action, 63 const DevToolsRemoteMessage& message) 64 : action_(action), 65 message_(message) {} 66 67ListenSocketTestAction::~ListenSocketTestAction() {} 68 69ListenSocket* DevToolsRemoteListenSocketTester::DoListen() { 70 return DevToolsRemoteListenSocket::Listen(kLoopback, kTestPort, this); 71} 72 73DevToolsRemoteListenSocketTester::DevToolsRemoteListenSocketTester() 74 : semaphore_(NULL), 75 thread_(NULL), 76 loop_(NULL), 77 server_(NULL), 78 connection_(NULL), 79 test_socket_(INVALID_SOCKET) { 80 memset(&lock_, 0, sizeof(lock_)); 81} 82 83void DevToolsRemoteListenSocketTester::SetUp() { 84#if defined(OS_WIN) 85 InitializeCriticalSection(&lock_); 86 semaphore_ = CreateSemaphore(NULL, 0, kMaxQueueSize, NULL); 87 server_ = NULL; 88 net::EnsureWinsockInit(); 89#elif defined(OS_POSIX) 90 ASSERT_EQ(0, pthread_mutex_init(&lock_, NULL)); 91 sem_unlink(kSemaphoreName); 92 semaphore_ = sem_open(kSemaphoreName, O_CREAT, 0, 0); 93 ASSERT_NE(SEM_FAILED, semaphore_); 94#endif 95 base::Thread::Options options; 96 options.message_loop_type = MessageLoop::TYPE_IO; 97 thread_.reset(new base::Thread("socketio_test")); 98 thread_->StartWithOptions(options); 99 loop_ = static_cast<MessageLoopForIO*>(thread_->message_loop()); 100 101 loop_->PostTask(FROM_HERE, NewRunnableMethod( 102 this, &DevToolsRemoteListenSocketTester::Listen)); 103 104 // verify Listen succeeded 105 ASSERT_TRUE(NextAction(TestTimeouts::action_timeout_ms())); 106 ASSERT_FALSE(server_ == NULL); 107 ASSERT_EQ(ACTION_LISTEN, last_action_.type()); 108 109 // verify the connect/accept and setup test_socket_ 110 test_socket_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 111 ASSERT_NE(INVALID_SOCKET, test_socket_); 112 struct sockaddr_in client; 113 client.sin_family = AF_INET; 114 client.sin_addr.s_addr = inet_addr(kLoopback); 115 client.sin_port = htons(kTestPort); 116 int ret = HANDLE_EINTR(connect(test_socket_, 117 reinterpret_cast<sockaddr*>(&client), 118 sizeof(client))); 119 ASSERT_NE(ret, SOCKET_ERROR); 120 121 net::SetNonBlocking(test_socket_); 122 ASSERT_TRUE(NextAction(TestTimeouts::action_timeout_ms())); 123 ASSERT_EQ(ACTION_ACCEPT, last_action_.type()); 124} 125 126void DevToolsRemoteListenSocketTester::TearDown() { 127 // verify close 128#if defined(OS_WIN) 129 closesocket(test_socket_); 130#elif defined(OS_POSIX) 131 int ret = HANDLE_EINTR(close(test_socket_)); 132 ASSERT_EQ(ret, 0); 133#endif 134 ASSERT_TRUE(NextAction(TestTimeouts::action_timeout_ms())); 135 ASSERT_EQ(ACTION_CLOSE, last_action_.type()); 136 137 loop_->PostTask(FROM_HERE, NewRunnableMethod( 138 this, &DevToolsRemoteListenSocketTester::Shutdown)); 139 ASSERT_TRUE(NextAction(TestTimeouts::action_timeout_ms())); 140 ASSERT_EQ(ACTION_SHUTDOWN, last_action_.type()); 141 142#if defined(OS_WIN) 143 CloseHandle(semaphore_); 144 semaphore_ = 0; 145 DeleteCriticalSection(&lock_); 146#elif defined(OS_POSIX) 147 ASSERT_EQ(0, pthread_mutex_lock(&lock_)); 148 semaphore_ = NULL; 149 ASSERT_EQ(0, pthread_mutex_unlock(&lock_)); 150 ASSERT_EQ(0, sem_unlink(kSemaphoreName)); 151 ASSERT_EQ(0, pthread_mutex_destroy(&lock_)); 152#endif 153 154 thread_.reset(); 155 loop_ = NULL; 156} 157 158void DevToolsRemoteListenSocketTester::ReportAction( 159 const ListenSocketTestAction& action) { 160#if defined(OS_WIN) 161 EnterCriticalSection(&lock_); 162 queue_.push_back(action); 163 LeaveCriticalSection(&lock_); 164 ReleaseSemaphore(semaphore_, 1, NULL); 165#elif defined(OS_POSIX) 166 ASSERT_EQ(0, pthread_mutex_lock(&lock_)); 167 queue_.push_back(action); 168 ASSERT_EQ(0, pthread_mutex_unlock(&lock_)); 169 ASSERT_EQ(0, sem_post(semaphore_)); 170#endif 171} 172 173bool DevToolsRemoteListenSocketTester::NextAction(int timeout) { 174#if defined(OS_WIN) 175 DWORD ret = ::WaitForSingleObject(semaphore_, timeout); 176 if (ret != WAIT_OBJECT_0) 177 return false; 178 EnterCriticalSection(&lock_); 179 if (queue_.empty()) { 180 LeaveCriticalSection(&lock_); 181 return false; 182 } 183 last_action_ = queue_.front(); 184 queue_.pop_front(); 185 LeaveCriticalSection(&lock_); 186 return true; 187#elif defined(OS_POSIX) 188 if (semaphore_ == SEM_FAILED) 189 return false; 190 while (true) { 191 int result = sem_trywait(semaphore_); 192 base::PlatformThread::Sleep(1); // 1MS sleep 193 timeout--; 194 if (timeout <= 0) 195 return false; 196 if (result == 0) 197 break; 198 } 199 pthread_mutex_lock(&lock_); 200 if (queue_.empty()) { 201 pthread_mutex_unlock(&lock_); 202 return false; 203 } 204 last_action_ = queue_.front(); 205 queue_.pop_front(); 206 pthread_mutex_unlock(&lock_); 207 return true; 208#endif 209} 210 211int DevToolsRemoteListenSocketTester::ClearTestSocket() { 212 char buf[kReadBufSize]; 213 int len_ret = 0; 214 int time_out = 0; 215 do { 216 int len = HANDLE_EINTR(recv(test_socket_, buf, kReadBufSize, 0)); 217#if defined(OS_WIN) 218 if (len == SOCKET_ERROR) { 219 int err = WSAGetLastError(); 220 if (err == WSAEWOULDBLOCK) { 221#elif defined(OS_POSIX) 222 if (len == SOCKET_ERROR) { 223 if (errno == EWOULDBLOCK || errno == EAGAIN) { 224#endif 225 base::PlatformThread::Sleep(1); 226 time_out++; 227 if (time_out > 10) 228 break; 229 continue; // still trying 230 } 231 } else if (len == 0) { 232 // socket closed 233 break; 234 } else { 235 time_out = 0; 236 len_ret += len; 237 } 238 } while (true); 239 return len_ret; 240} 241 242void DevToolsRemoteListenSocketTester::Shutdown() { 243 server_->Release(); 244 server_ = NULL; 245 ReportAction(ListenSocketTestAction(ACTION_SHUTDOWN)); 246} 247 248void DevToolsRemoteListenSocketTester::Listen() { 249 server_ = DoListen(); 250 server_->AddRef(); 251 ReportAction(ListenSocketTestAction(ACTION_LISTEN)); 252} 253 254void DevToolsRemoteListenSocketTester::SendFromTester() { 255 connection_->Send(kChromeDevToolsHandshake); 256 ReportAction(ListenSocketTestAction(ACTION_SEND)); 257} 258 259void DevToolsRemoteListenSocketTester::OnAcceptConnection( 260 ListenSocket* connection) { 261 connection_ = connection; 262 ReportAction(ListenSocketTestAction(ACTION_ACCEPT)); 263} 264 265void DevToolsRemoteListenSocketTester::OnConnectionLost() { 266 connection_ = NULL; 267 ReportAction(ListenSocketTestAction(ACTION_CLOSE)); 268} 269 270void DevToolsRemoteListenSocketTester::HandleMessage( 271 const DevToolsRemoteMessage& message) { 272 ReportAction(ListenSocketTestAction(ACTION_READ_MESSAGE, message)); 273} 274 275bool DevToolsRemoteListenSocketTester::Send(SOCKET sock, 276 const std::string& str) { 277 int len = static_cast<int>(str.length()); 278 int send_len = HANDLE_EINTR(send(sock, str.data(), len, 0)); 279 if (send_len == SOCKET_ERROR) { 280 LOG(ERROR) << "send failed: " << errno; 281 return false; 282 } else if (send_len != len) { 283 return false; 284 } 285 return true; 286} 287 288void DevToolsRemoteListenSocketTester::TestClientSend() { 289 ASSERT_TRUE(Send(test_socket_, kChromeDevToolsHandshake)); 290 { 291 ASSERT_TRUE(Send(test_socket_, kSimpleMessagePart1)); 292 // sleep for 10ms to test message split between \r and \n 293 base::PlatformThread::Sleep(10); 294 ASSERT_TRUE(Send(test_socket_, kSimpleMessagePart2)); 295 ASSERT_TRUE(NextAction(TestTimeouts::action_timeout_ms())); 296 ASSERT_EQ(ACTION_READ_MESSAGE, last_action_.type()); 297 const DevToolsRemoteMessage& message = last_action_.message(); 298 ASSERT_STREQ("V8Debugger", message.GetHeaderWithEmptyDefault( 299 DevToolsRemoteMessageHeaders::kTool).c_str()); 300 ASSERT_STREQ("2", message.GetHeaderWithEmptyDefault( 301 DevToolsRemoteMessageHeaders::kDestination).c_str()); 302 ASSERT_STREQ("0", message.GetHeaderWithEmptyDefault( 303 DevToolsRemoteMessageHeaders::kContentLength).c_str()); 304 ASSERT_EQ(0, static_cast<int>(message.content().size())); 305 } 306 ASSERT_TRUE(Send(test_socket_, kTwoMessages)); 307 { 308 ASSERT_TRUE(NextAction(TestTimeouts::action_timeout_ms())); 309 ASSERT_EQ(ACTION_READ_MESSAGE, last_action_.type()); 310 const DevToolsRemoteMessage& message = last_action_.message(); 311 ASSERT_STREQ("DevToolsService", message.tool().c_str()); 312 ASSERT_STREQ("", message.destination().c_str()); 313 ASSERT_EQ(300, message.content_length()); 314 const std::string& content = message.content(); 315 ASSERT_EQ(300, static_cast<int>(content.size())); 316 for (int i = 0; i < 300; ++i) { 317 ASSERT_EQ('0', content[i]); 318 } 319 } 320 { 321 ASSERT_TRUE(NextAction(TestTimeouts::action_timeout_ms())); 322 ASSERT_EQ(ACTION_READ_MESSAGE, last_action_.type()); 323 const DevToolsRemoteMessage& message = last_action_.message(); 324 ASSERT_STREQ("V8Debugger", message.GetHeaderWithEmptyDefault( 325 DevToolsRemoteMessageHeaders::kTool).c_str()); 326 ASSERT_STREQ("1", message.GetHeaderWithEmptyDefault( 327 DevToolsRemoteMessageHeaders::kDestination).c_str()); 328 ASSERT_STREQ("0", message.GetHeaderWithEmptyDefault( 329 DevToolsRemoteMessageHeaders::kContentLength).c_str()); 330 const std::string& content = message.content(); 331 ASSERT_EQ(0, static_cast<int>(content.size())); 332 } 333} 334 335void DevToolsRemoteListenSocketTester::TestServerSend() { 336 loop_->PostTask(FROM_HERE, NewRunnableMethod( 337 this, &DevToolsRemoteListenSocketTester::SendFromTester)); 338 ASSERT_TRUE(NextAction(TestTimeouts::action_timeout_ms())); 339 ASSERT_EQ(ACTION_SEND, last_action_.type()); 340 // TODO(erikkay): Without this sleep, the recv seems to fail a small amount 341 // of the time. I could fix this by making the socket blocking, but then 342 // this test might hang in the case of errors. It would be nice to do 343 // something that felt more reliable here. 344 base::PlatformThread::Sleep(10); // sleep for 10ms 345 const int buf_len = 200; 346 char buf[buf_len+1]; 347 int recv_len = HANDLE_EINTR(recv(test_socket_, buf, buf_len, 0)); 348 ASSERT_NE(recv_len, SOCKET_ERROR); 349 buf[recv_len] = 0; 350 ASSERT_STREQ(buf, kChromeDevToolsHandshake); 351} 352 353DevToolsRemoteListenSocketTester::~DevToolsRemoteListenSocketTester() {} 354 355class DevToolsRemoteListenSocketTest: public PlatformTest { 356 public: 357 DevToolsRemoteListenSocketTest() { 358 tester_ = NULL; 359 } 360 361 virtual void SetUp() { 362 PlatformTest::SetUp(); 363 tester_ = new DevToolsRemoteListenSocketTester(); 364 tester_->SetUp(); 365 } 366 367 virtual void TearDown() { 368 PlatformTest::TearDown(); 369 tester_->TearDown(); 370 tester_ = NULL; 371 } 372 373 scoped_refptr<DevToolsRemoteListenSocketTester> tester_; 374}; 375 376// This test is flaky; see comment in ::TestServerSend. 377TEST_F(DevToolsRemoteListenSocketTest, ServerSend) { 378 tester_->TestServerSend(); 379} 380 381TEST_F(DevToolsRemoteListenSocketTest, ClientSend) { 382 tester_->TestClientSend(); 383} 384