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