Home | History | Annotate | Download | only in debugger
      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 
     18 const int DevToolsRemoteListenSocketTester::kTestPort = 9999;
     19 
     20 static const int kReadBufSize = 1024;
     21 static const char* kChromeDevToolsHandshake = "ChromeDevToolsHandshake\r\n";
     22 static const char* kSimpleMessagePart1 =
     23     "Tool:V8Debugger\r\n"
     24     "Destination:2\r";
     25 static const char* kSimpleMessagePart2 =
     26     "\n"
     27     "Content-Length:0\r\n"
     28     "\r\n";
     29 static 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 
     44 static const int kMaxQueueSize = 20;
     45 static const char* kLoopback = "127.0.0.1";
     46 #if defined(OS_POSIX)
     47 static const char* kSemaphoreName = "chromium.listen_socket";
     48 #endif
     49 
     50 
     51 ListenSocketTestAction::ListenSocketTestAction() : action_(ACTION_NONE) {}
     52 
     53 ListenSocketTestAction::ListenSocketTestAction(ActionType action)
     54       : action_(action) {}
     55 
     56 ListenSocketTestAction::ListenSocketTestAction(ActionType action,
     57                                                std::string data)
     58     : action_(action),
     59       data_(data) {}
     60 
     61 ListenSocketTestAction::ListenSocketTestAction(
     62     ActionType action,
     63     const DevToolsRemoteMessage& message)
     64     : action_(action),
     65       message_(message) {}
     66 
     67 ListenSocketTestAction::~ListenSocketTestAction() {}
     68 
     69 ListenSocket* DevToolsRemoteListenSocketTester::DoListen() {
     70   return DevToolsRemoteListenSocket::Listen(kLoopback, kTestPort, this);
     71 }
     72 
     73 DevToolsRemoteListenSocketTester::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 
     83 void 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 
    126 void 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 
    158 void 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 
    173 bool 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 
    211 int 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 
    242 void DevToolsRemoteListenSocketTester::Shutdown() {
    243   server_->Release();
    244   server_ = NULL;
    245   ReportAction(ListenSocketTestAction(ACTION_SHUTDOWN));
    246 }
    247 
    248 void DevToolsRemoteListenSocketTester::Listen() {
    249   server_ = DoListen();
    250   server_->AddRef();
    251   ReportAction(ListenSocketTestAction(ACTION_LISTEN));
    252 }
    253 
    254 void DevToolsRemoteListenSocketTester::SendFromTester() {
    255   connection_->Send(kChromeDevToolsHandshake);
    256   ReportAction(ListenSocketTestAction(ACTION_SEND));
    257 }
    258 
    259 void DevToolsRemoteListenSocketTester::OnAcceptConnection(
    260     ListenSocket* connection) {
    261   connection_ = connection;
    262   ReportAction(ListenSocketTestAction(ACTION_ACCEPT));
    263 }
    264 
    265 void DevToolsRemoteListenSocketTester::OnConnectionLost() {
    266   connection_ = NULL;
    267   ReportAction(ListenSocketTestAction(ACTION_CLOSE));
    268 }
    269 
    270 void DevToolsRemoteListenSocketTester::HandleMessage(
    271     const DevToolsRemoteMessage& message) {
    272   ReportAction(ListenSocketTestAction(ACTION_READ_MESSAGE, message));
    273 }
    274 
    275 bool 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 
    288 void 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 
    335 void 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 
    353 DevToolsRemoteListenSocketTester::~DevToolsRemoteListenSocketTester() {}
    354 
    355 class 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.
    377 TEST_F(DevToolsRemoteListenSocketTest, ServerSend) {
    378   tester_->TestServerSend();
    379 }
    380 
    381 TEST_F(DevToolsRemoteListenSocketTest, ClientSend) {
    382   tester_->TestClientSend();
    383 }
    384