1 // Copyright (c) 2012 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.
5 #include "base/sync_socket.h"
11 #include "base/bind.h"
12 #include "base/location.h"
13 #include "base/single_thread_task_runner.h"
14 #include "base/threading/thread.h"
15 #include "ipc/ipc_test_base.h"
16 #include "testing/gtest/include/gtest/gtest.h"
19 #include "base/file_descriptor_posix.h"
22 // IPC messages for testing ----------------------------------------------------
24 #define IPC_MESSAGE_IMPL
25 #include "ipc/ipc_message_macros.h"
27 #define IPC_MESSAGE_START TestMsgStart
29 // Message class to pass a base::SyncSocket::Handle to another process. This
30 // is not as easy as it sounds, because of the differences in transferring
31 // Windows HANDLEs versus posix file descriptors.
33 IPC_MESSAGE_CONTROL1(MsgClassSetHandle
, base::SyncSocket::Handle
)
34 #elif defined(OS_POSIX)
35 IPC_MESSAGE_CONTROL1(MsgClassSetHandle
, base::FileDescriptor
)
38 // Message class to pass a response to the server.
39 IPC_MESSAGE_CONTROL1(MsgClassResponse
, std::string
)
41 // Message class to tell the server to shut down.
42 IPC_MESSAGE_CONTROL0(MsgClassShutdown
)
44 // -----------------------------------------------------------------------------
48 const char kHelloString
[] = "Hello, SyncSocket Client";
49 const size_t kHelloStringLength
= arraysize(kHelloString
);
51 // The SyncSocket server listener class processes two sorts of
52 // messages from the client.
53 class SyncSocketServerListener
: public IPC::Listener
{
55 SyncSocketServerListener() : chan_(NULL
) {
58 void Init(IPC::Channel
* chan
) {
62 bool OnMessageReceived(const IPC::Message
& msg
) override
{
63 if (msg
.routing_id() == MSG_ROUTING_CONTROL
) {
64 IPC_BEGIN_MESSAGE_MAP(SyncSocketServerListener
, msg
)
65 IPC_MESSAGE_HANDLER(MsgClassSetHandle
, OnMsgClassSetHandle
)
66 IPC_MESSAGE_HANDLER(MsgClassShutdown
, OnMsgClassShutdown
)
73 // This sort of message is sent first, causing the transfer of
74 // the handle for the SyncSocket. This message sends a buffer
75 // on the SyncSocket and then sends a response to the client.
77 void OnMsgClassSetHandle(const base::SyncSocket::Handle handle
) {
80 #elif defined(OS_POSIX)
81 void OnMsgClassSetHandle(const base::FileDescriptor
& fd_struct
) {
82 SetHandle(fd_struct
.fd
);
85 # error "What platform?"
86 #endif // defined(OS_WIN)
88 void SetHandle(base::SyncSocket::Handle handle
) {
89 base::SyncSocket
sync_socket(handle
);
90 EXPECT_EQ(sync_socket
.Send(kHelloString
, kHelloStringLength
),
92 IPC::Message
* msg
= new MsgClassResponse(kHelloString
);
93 EXPECT_TRUE(chan_
->Send(msg
));
96 // When the client responds, it sends back a shutdown message,
97 // which causes the message loop to exit.
98 void OnMsgClassShutdown() {
99 base::MessageLoop::current()->Quit();
104 DISALLOW_COPY_AND_ASSIGN(SyncSocketServerListener
);
107 // Runs the fuzzing server child mode. Returns when the preset number of
108 // messages have been received.
109 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SyncSocketServerClient
) {
110 base::MessageLoopForIO main_message_loop
;
111 SyncSocketServerListener listener
;
112 scoped_ptr
<IPC::Channel
> channel(IPC::Channel::CreateClient(
113 IPCTestBase::GetChannelName("SyncSocketServerClient"),
115 EXPECT_TRUE(channel
->Connect());
116 listener
.Init(channel
.get());
117 base::MessageLoop::current()->Run();
121 // The SyncSocket client listener only processes one sort of message,
122 // a response from the server.
123 class SyncSocketClientListener
: public IPC::Listener
{
125 SyncSocketClientListener() {
128 void Init(base::SyncSocket
* socket
, IPC::Channel
* chan
) {
133 bool OnMessageReceived(const IPC::Message
& msg
) override
{
134 if (msg
.routing_id() == MSG_ROUTING_CONTROL
) {
135 IPC_BEGIN_MESSAGE_MAP(SyncSocketClientListener
, msg
)
136 IPC_MESSAGE_HANDLER(MsgClassResponse
, OnMsgClassResponse
)
137 IPC_END_MESSAGE_MAP()
143 // When a response is received from the server, it sends the same
144 // string as was written on the SyncSocket. These are compared
145 // and a shutdown message is sent back to the server.
146 void OnMsgClassResponse(const std::string
& str
) {
147 // We rely on the order of sync_socket.Send() and chan_->Send() in
148 // the SyncSocketServerListener object.
149 EXPECT_EQ(kHelloStringLength
, socket_
->Peek());
150 char buf
[kHelloStringLength
];
151 socket_
->Receive(static_cast<void*>(buf
), kHelloStringLength
);
152 EXPECT_EQ(strcmp(str
.c_str(), buf
), 0);
153 // After receiving from the socket there should be no bytes left.
154 EXPECT_EQ(0U, socket_
->Peek());
155 IPC::Message
* msg
= new MsgClassShutdown();
156 EXPECT_TRUE(chan_
->Send(msg
));
157 base::MessageLoop::current()->Quit();
160 base::SyncSocket
* socket_
;
163 DISALLOW_COPY_AND_ASSIGN(SyncSocketClientListener
);
166 class SyncSocketTest
: public IPCTestBase
{
169 TEST_F(SyncSocketTest
, SanityTest
) {
170 Init("SyncSocketServerClient");
172 SyncSocketClientListener listener
;
173 CreateChannel(&listener
);
174 ASSERT_TRUE(StartClient());
175 // Create a pair of SyncSockets.
176 base::SyncSocket pair
[2];
177 base::SyncSocket::CreatePair(&pair
[0], &pair
[1]);
178 // Immediately after creation there should be no pending bytes.
179 EXPECT_EQ(0U, pair
[0].Peek());
180 EXPECT_EQ(0U, pair
[1].Peek());
181 base::SyncSocket::Handle target_handle
;
182 // Connect the channel and listener.
183 ASSERT_TRUE(ConnectChannel());
184 listener
.Init(&pair
[0], channel());
186 // On windows we need to duplicate the handle into the server process.
187 BOOL retval
= DuplicateHandle(GetCurrentProcess(), pair
[1].handle(),
188 client_process().Handle(), &target_handle
,
189 0, FALSE
, DUPLICATE_SAME_ACCESS
);
191 // Set up a message to pass the handle to the server.
192 IPC::Message
* msg
= new MsgClassSetHandle(target_handle
);
194 target_handle
= pair
[1].handle();
195 // Set up a message to pass the handle to the server.
196 base::FileDescriptor
filedesc(target_handle
, false);
197 IPC::Message
* msg
= new MsgClassSetHandle(filedesc
);
198 #endif // defined(OS_WIN)
199 EXPECT_TRUE(sender()->Send(msg
));
200 // Use the current thread as the I/O thread.
201 base::MessageLoop::current()->Run();
205 EXPECT_TRUE(WaitForClientShutdown());
209 // A blocking read operation that will block the thread until it receives
210 // |length| bytes of packets or Shutdown() is called on another thread.
211 static void BlockingRead(base::SyncSocket
* socket
, char* buf
,
212 size_t length
, size_t* received
) {
214 // Notify the parent thread that we're up and running.
215 socket
->Send(kHelloString
, kHelloStringLength
);
216 *received
= socket
->Receive(buf
, length
);
219 // Tests that we can safely end a blocking Receive operation on one thread
220 // from another thread by disconnecting (but not closing) the socket.
221 TEST_F(SyncSocketTest
, DisconnectTest
) {
222 base::CancelableSyncSocket pair
[2];
223 ASSERT_TRUE(base::CancelableSyncSocket::CreatePair(&pair
[0], &pair
[1]));
225 base::Thread
worker("BlockingThread");
228 // Try to do a blocking read from one of the sockets on the worker thread.
230 size_t received
= 1U; // Initialize to an unexpected value.
231 worker
.task_runner()->PostTask(
233 base::Bind(&BlockingRead
, &pair
[0], &buf
[0], arraysize(buf
), &received
));
235 // Wait for the worker thread to say hello.
236 char hello
[kHelloStringLength
] = {0};
237 pair
[1].Receive(&hello
[0], sizeof(hello
));
238 EXPECT_EQ(0, strcmp(hello
, kHelloString
));
239 // Give the worker a chance to start Receive().
240 base::PlatformThread::YieldCurrentThread();
242 // Now shut down the socket that the thread is issuing a blocking read on
243 // which should cause Receive to return with an error.
248 EXPECT_EQ(0U, received
);
251 // Tests that read is a blocking operation.
252 TEST_F(SyncSocketTest
, BlockingReceiveTest
) {
253 base::CancelableSyncSocket pair
[2];
254 ASSERT_TRUE(base::CancelableSyncSocket::CreatePair(&pair
[0], &pair
[1]));
256 base::Thread
worker("BlockingThread");
259 // Try to do a blocking read from one of the sockets on the worker thread.
260 char buf
[kHelloStringLength
] = {0};
261 size_t received
= 1U; // Initialize to an unexpected value.
262 worker
.task_runner()->PostTask(FROM_HERE
,
263 base::Bind(&BlockingRead
, &pair
[0], &buf
[0],
264 kHelloStringLength
, &received
));
266 // Wait for the worker thread to say hello.
267 char hello
[kHelloStringLength
] = {0};
268 pair
[1].Receive(&hello
[0], sizeof(hello
));
269 EXPECT_EQ(0, strcmp(hello
, kHelloString
));
270 // Give the worker a chance to start Receive().
271 base::PlatformThread::YieldCurrentThread();
273 // Send a message to the socket on the blocking thead, it should free the
274 // socket from Receive().
275 pair
[1].Send(kHelloString
, kHelloStringLength
);
278 // Verify the socket has received the message.
279 EXPECT_TRUE(strcmp(buf
, kHelloString
) == 0);
280 EXPECT_EQ(kHelloStringLength
, received
);
283 // Tests that the write operation is non-blocking and returns immediately
284 // when there is insufficient space in the socket's buffer.
285 TEST_F(SyncSocketTest
, NonBlockingWriteTest
) {
286 base::CancelableSyncSocket pair
[2];
287 ASSERT_TRUE(base::CancelableSyncSocket::CreatePair(&pair
[0], &pair
[1]));
289 // Fill up the buffer for one of the socket, Send() should not block the
290 // thread even when the buffer is full.
291 while (pair
[0].Send(kHelloString
, kHelloStringLength
) != 0) {}
293 // Data should be avialble on another socket.
294 size_t bytes_in_buffer
= pair
[1].Peek();
295 EXPECT_NE(bytes_in_buffer
, 0U);
297 // No more data can be written to the buffer since socket has been full,
298 // verify that the amount of avialble data on another socket is unchanged.
299 EXPECT_EQ(0U, pair
[0].Send(kHelloString
, kHelloStringLength
));
300 EXPECT_EQ(bytes_in_buffer
, pair
[1].Peek());
302 // Read from another socket to free some space for a new write.
303 char hello
[kHelloStringLength
] = {0};
304 pair
[1].Receive(&hello
[0], sizeof(hello
));
306 // Should be able to write more data to the buffer now.
307 EXPECT_EQ(kHelloStringLength
, pair
[0].Send(kHelloString
, kHelloStringLength
));