xref: /aosp_15_r20/external/cronet/ipc/sync_socket_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
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 "base/sync_socket.h"
6 
7 #include <stddef.h>
8 #include <stdio.h>
9 
10 #include <memory>
11 #include <sstream>
12 #include <string>
13 
14 #include "base/containers/span.h"
15 #include "base/functional/bind.h"
16 #include "base/location.h"
17 #include "base/memory/raw_ptr.h"
18 #include "base/run_loop.h"
19 #include "base/task/single_thread_task_runner.h"
20 #include "base/threading/thread.h"
21 #include "base/types/fixed_array.h"
22 #include "build/build_config.h"
23 #include "ipc/ipc_test_base.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 
26 #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
27 #include "base/file_descriptor_posix.h"
28 #endif
29 
30 // IPC messages for testing ----------------------------------------------------
31 
32 #define IPC_MESSAGE_IMPL
33 #include "ipc/ipc_message_macros.h"
34 #include "ipc/ipc_message_start.h"
35 
36 #define IPC_MESSAGE_START TestMsgStart
37 
38 // Message class to pass a base::SyncSocket::Handle to another process.  This
39 // is not as easy as it sounds, because of the differences in transferring
40 // Windows HANDLEs versus posix file descriptors.
41 #if BUILDFLAG(IS_WIN)
42 IPC_MESSAGE_CONTROL1(MsgClassSetHandle, base::SyncSocket::Handle)
43 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
44 IPC_MESSAGE_CONTROL1(MsgClassSetHandle, base::FileDescriptor)
45 #endif
46 
47 // Message class to pass a response to the server.
48 IPC_MESSAGE_CONTROL1(MsgClassResponse, std::string)
49 
50 // Message class to tell the server to shut down.
51 IPC_MESSAGE_CONTROL0(MsgClassShutdown)
52 
53 // -----------------------------------------------------------------------------
54 
55 namespace {
56 
57 const char kHelloString[] = "Hello, SyncSocket Client";
58 const size_t kHelloStringLength = std::size(kHelloString);
59 
60 // The SyncSocket server listener class processes two sorts of
61 // messages from the client.
62 class SyncSocketServerListener : public IPC::Listener {
63  public:
SyncSocketServerListener()64   SyncSocketServerListener() : chan_(nullptr) {}
65 
66   SyncSocketServerListener(const SyncSocketServerListener&) = delete;
67   SyncSocketServerListener& operator=(const SyncSocketServerListener&) = delete;
68 
Init(IPC::Channel * chan)69   void Init(IPC::Channel* chan) {
70     chan_ = chan;
71   }
72 
OnMessageReceived(const IPC::Message & msg)73   bool OnMessageReceived(const IPC::Message& msg) override {
74     if (msg.routing_id() == MSG_ROUTING_CONTROL) {
75       IPC_BEGIN_MESSAGE_MAP(SyncSocketServerListener, msg)
76         IPC_MESSAGE_HANDLER(MsgClassSetHandle, OnMsgClassSetHandle)
77         IPC_MESSAGE_HANDLER(MsgClassShutdown, OnMsgClassShutdown)
78       IPC_END_MESSAGE_MAP()
79     }
80     return true;
81   }
set_quit_closure(base::OnceClosure quit_closure)82   void set_quit_closure(base::OnceClosure quit_closure) {
83     quit_closure_ = std::move(quit_closure);
84   }
85 
86  private:
87   // This sort of message is sent first, causing the transfer of
88   // the handle for the SyncSocket.  This message sends a buffer
89   // on the SyncSocket and then sends a response to the client.
90 #if BUILDFLAG(IS_WIN)
OnMsgClassSetHandle(const base::SyncSocket::Handle handle)91   void OnMsgClassSetHandle(const base::SyncSocket::Handle handle) {
92     SetHandle(handle);
93   }
94 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
95   void OnMsgClassSetHandle(const base::FileDescriptor& fd_struct) {
96     SetHandle(fd_struct.fd);
97   }
98 #else
99 # error "What platform?"
100 #endif  // BUILDFLAG(IS_WIN)
101 
SetHandle(base::SyncSocket::Handle handle)102   void SetHandle(base::SyncSocket::Handle handle) {
103     base::SyncSocket sync_socket(handle);
104     auto bytes_to_send = base::as_byte_span(kHelloString);
105     EXPECT_EQ(sync_socket.Send(bytes_to_send), bytes_to_send.size());
106     IPC::Message* msg = new MsgClassResponse(kHelloString);
107     EXPECT_TRUE(chan_->Send(msg));
108   }
109 
110   // When the client responds, it sends back a shutdown message,
111   // which causes the message loop to exit.
OnMsgClassShutdown()112   void OnMsgClassShutdown() { std::move(quit_closure_).Run(); }
113 
114   raw_ptr<IPC::Channel> chan_;
115   base::OnceClosure quit_closure_;
116 };
117 
118 // Runs the fuzzing server child mode. Returns when the preset number of
119 // messages have been received.
DEFINE_IPC_CHANNEL_MOJO_TEST_CLIENT(SyncSocketServerClient)120 DEFINE_IPC_CHANNEL_MOJO_TEST_CLIENT(SyncSocketServerClient) {
121   SyncSocketServerListener listener;
122   base::RunLoop loop;
123   listener.set_quit_closure(loop.QuitWhenIdleClosure());
124   Connect(&listener);
125   listener.Init(channel());
126   loop.Run();
127   Close();
128 }
129 
130 // The SyncSocket client listener only processes one sort of message,
131 // a response from the server.
132 class SyncSocketClientListener : public IPC::Listener {
133  public:
134   SyncSocketClientListener() = default;
135 
136   SyncSocketClientListener(const SyncSocketClientListener&) = delete;
137   SyncSocketClientListener& operator=(const SyncSocketClientListener&) = delete;
138 
Init(base::SyncSocket * socket,IPC::Channel * chan)139   void Init(base::SyncSocket* socket, IPC::Channel* chan) {
140     socket_ = socket;
141     chan_ = chan;
142   }
143 
OnMessageReceived(const IPC::Message & msg)144   bool OnMessageReceived(const IPC::Message& msg) override {
145     if (msg.routing_id() == MSG_ROUTING_CONTROL) {
146       IPC_BEGIN_MESSAGE_MAP(SyncSocketClientListener, msg)
147         IPC_MESSAGE_HANDLER(MsgClassResponse, OnMsgClassResponse)
148       IPC_END_MESSAGE_MAP()
149     }
150     return true;
151   }
set_quit_closure(base::OnceClosure quit_closure)152   void set_quit_closure(base::OnceClosure quit_closure) {
153     quit_closure_ = std::move(quit_closure);
154   }
155 
156  private:
157   // When a response is received from the server, it sends the same
158   // string as was written on the SyncSocket.  These are compared
159   // and a shutdown message is sent back to the server.
OnMsgClassResponse(const std::string & str)160   void OnMsgClassResponse(const std::string& str) {
161     // Account for the terminating null byte.
162     size_t expected_bytes_to_receive = str.length() + 1;
163     // We rely on the order of sync_socket.Send() and chan_->Send() in
164     // the SyncSocketServerListener object.
165     EXPECT_EQ(socket_->Peek(), expected_bytes_to_receive);
166     base::FixedArray<char> buf(expected_bytes_to_receive);
167     socket_->Receive(base::as_writable_bytes(base::make_span(buf)));
168     EXPECT_EQ(strcmp(str.c_str(), buf.data()), 0);
169     // After receiving from the socket there should be no bytes left.
170     EXPECT_EQ(0U, socket_->Peek());
171     IPC::Message* msg = new MsgClassShutdown();
172     EXPECT_TRUE(chan_->Send(msg));
173     std::move(quit_closure_).Run();
174   }
175 
176   raw_ptr<base::SyncSocket> socket_;
177   raw_ptr<IPC::Channel, DanglingUntriaged> chan_;
178   base::OnceClosure quit_closure_;
179 };
180 
181 using SyncSocketTest = IPCChannelMojoTestBase;
182 
TEST_F(SyncSocketTest,SanityTest)183 TEST_F(SyncSocketTest, SanityTest) {
184   Init("SyncSocketServerClient");
185   base::RunLoop loop;
186 
187   SyncSocketClientListener listener;
188   listener.set_quit_closure(loop.QuitWhenIdleClosure());
189   CreateChannel(&listener);
190   // Create a pair of SyncSockets.
191   base::SyncSocket pair[2];
192   base::SyncSocket::CreatePair(&pair[0], &pair[1]);
193   // Immediately after creation there should be no pending bytes.
194   EXPECT_EQ(0U, pair[0].Peek());
195   EXPECT_EQ(0U, pair[1].Peek());
196   base::SyncSocket::Handle target_handle;
197   // Connect the channel and listener.
198   ASSERT_TRUE(ConnectChannel());
199   listener.Init(&pair[0], channel());
200 #if BUILDFLAG(IS_WIN)
201   // On windows we need to duplicate the handle into the server process.
202   BOOL retval = DuplicateHandle(GetCurrentProcess(), pair[1].handle(),
203                                 client_process().Handle(), &target_handle,
204                                 0, FALSE, DUPLICATE_SAME_ACCESS);
205   EXPECT_TRUE(retval);
206   // Set up a message to pass the handle to the server.
207   IPC::Message* msg = new MsgClassSetHandle(target_handle);
208 #else
209   target_handle = pair[1].handle();
210   // Set up a message to pass the handle to the server.
211   base::FileDescriptor filedesc(target_handle, false);
212   IPC::Message* msg = new MsgClassSetHandle(filedesc);
213 #endif  // BUILDFLAG(IS_WIN)
214   EXPECT_TRUE(sender()->Send(msg));
215   // Use the current thread as the I/O thread.
216   loop.Run();
217   // Shut down.
218   pair[0].Close();
219   pair[1].Close();
220   EXPECT_TRUE(WaitForClientShutdown());
221   DestroyChannel();
222 }
223 
224 // A blocking read operation that will block the thread until it receives
225 // |buffer|'s length bytes of packets or Shutdown() is called on another thread.
BlockingRead(base::SyncSocket * socket,base::span<uint8_t> buffer,size_t * received)226 static void BlockingRead(base::SyncSocket* socket,
227                          base::span<uint8_t> buffer,
228                          size_t* received) {
229   // Notify the parent thread that we're up and running.
230   socket->Send(base::as_byte_span(kHelloString));
231   *received = socket->Receive(buffer);
232 }
233 
234 // Tests that we can safely end a blocking Receive operation on one thread
235 // from another thread by disconnecting (but not closing) the socket.
TEST_F(SyncSocketTest,DisconnectTest)236 TEST_F(SyncSocketTest, DisconnectTest) {
237   base::CancelableSyncSocket pair[2];
238   ASSERT_TRUE(base::CancelableSyncSocket::CreatePair(&pair[0], &pair[1]));
239 
240   base::Thread worker("BlockingThread");
241   worker.Start();
242 
243   // Try to do a blocking read from one of the sockets on the worker thread.
244   char buf[0xff];
245   size_t received = 1U;  // Initialize to an unexpected value.
246   worker.task_runner()->PostTask(
247       FROM_HERE,
248       base::BindOnce(&BlockingRead, &pair[0],
249                      base::as_writable_bytes(base::make_span(buf)), &received));
250 
251   // Wait for the worker thread to say hello.
252   char hello[kHelloStringLength] = {0};
253   pair[1].Receive(base::as_writable_bytes(base::make_span(hello)));
254   EXPECT_EQ(strcmp(hello, kHelloString), 0);
255   // Give the worker a chance to start Receive().
256   base::PlatformThread::YieldCurrentThread();
257 
258   // Now shut down the socket that the thread is issuing a blocking read on
259   // which should cause Receive to return with an error.
260   pair[0].Shutdown();
261 
262   worker.Stop();
263 
264   EXPECT_EQ(0U, received);
265 }
266 
267 // Tests that read is a blocking operation.
TEST_F(SyncSocketTest,BlockingReceiveTest)268 TEST_F(SyncSocketTest, BlockingReceiveTest) {
269   base::CancelableSyncSocket pair[2];
270   ASSERT_TRUE(base::CancelableSyncSocket::CreatePair(&pair[0], &pair[1]));
271 
272   base::Thread worker("BlockingThread");
273   worker.Start();
274 
275   // Try to do a blocking read from one of the sockets on the worker thread.
276   char buf[kHelloStringLength] = {0};
277   size_t received = 1U;  // Initialize to an unexpected value.
278   worker.task_runner()->PostTask(
279       FROM_HERE,
280       base::BindOnce(&BlockingRead, &pair[0],
281                      base::as_writable_bytes(base::make_span(buf)), &received));
282 
283   // Wait for the worker thread to say hello.
284   char hello[kHelloStringLength] = {0};
285   pair[1].Receive(base::as_writable_bytes(base::make_span(hello)));
286   EXPECT_EQ(0, strcmp(hello, kHelloString));
287   // Give the worker a chance to start Receive().
288   base::PlatformThread::YieldCurrentThread();
289 
290   // Send a message to the socket on the blocking thead, it should free the
291   // socket from Receive().
292   auto bytes_to_send = base::as_byte_span(kHelloString);
293   pair[1].Send(bytes_to_send);
294   worker.Stop();
295 
296   // Verify the socket has received the message.
297   EXPECT_TRUE(strcmp(buf, kHelloString) == 0);
298   EXPECT_EQ(received, bytes_to_send.size());
299 }
300 
301 // Tests that the write operation is non-blocking and returns immediately
302 // when there is insufficient space in the socket's buffer.
TEST_F(SyncSocketTest,NonBlockingWriteTest)303 TEST_F(SyncSocketTest, NonBlockingWriteTest) {
304   base::CancelableSyncSocket pair[2];
305   ASSERT_TRUE(base::CancelableSyncSocket::CreatePair(&pair[0], &pair[1]));
306 
307   // Fill up the buffer for one of the socket, Send() should not block the
308   // thread even when the buffer is full.
309   auto bytes_to_send = base::as_byte_span(kHelloString);
310   while (pair[0].Send(bytes_to_send) != 0) {
311   }
312 
313   // Data should be avialble on another socket.
314   size_t bytes_in_buffer = pair[1].Peek();
315   EXPECT_NE(bytes_in_buffer, 0U);
316 
317   // No more data can be written to the buffer since socket has been full,
318   // verify that the amount of avialble data on another socket is unchanged.
319   EXPECT_EQ(pair[0].Send(bytes_to_send), 0U);
320   EXPECT_EQ(bytes_in_buffer, pair[1].Peek());
321 
322   // Read from another socket to free some space for a new write.
323   char hello[kHelloStringLength] = {0};
324   pair[1].Receive(base::as_writable_bytes(base::make_span(hello)));
325 
326   // Should be able to write more data to the buffer now.
327   EXPECT_EQ(pair[0].Send(bytes_to_send), bytes_to_send.size());
328 }
329 
330 }  // namespace
331