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