scclib
Stable Cloud Computing C++ Library
unix.cc
Go to the documentation of this file.
1 /*
2 BSD 3-Clause License
3 
4 Copyright (c) 2022, Stable Cloud Computing, Inc.
5 
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8 
9 1. Redistributions of source code must retain the above copyright notice, this
10  list of conditions and the following disclaimer.
11 
12 2. Redistributions in binary form must reproduce the above copyright notice,
13  this list of conditions and the following disclaimer in the documentation
14  and/or other materials provided with the distribution.
15 
16 3. Neither the name of the copyright holder nor the names of its
17  contributors may be used to endorse or promote products derived from
18  this software without specific prior written permission.
19 
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31 #include <net/unix.h>
32 #include <gtest/gtest.h>
33 #include <iostream>
34 #include <system_error>
35 #include <cerrno>
36 #include <thread>
37 #include <future>
38 #include <sstream>
39 #include <util/event.h>
40 #include <util/poller.h>
41 #include <net/inet.h>
42 #include <util/iostream.h>
43 #include <util/fs.h>
44 #include <util/logger.h>
45 
52 using std::cout;
53 using std::endl;
54 using std::string;
55 using std::async;
56 using std::stringstream;
58 using scc::util::Logger;
59 using scc::util::Event;
60 using scc::util::Poller;
61 using scc::net::UnixAddr;
64 
65 std::string server_addrname("/tmp/server.sock");
66 std::string client_addrname("/tmp/client.sock");
67 
68 struct unix_networking : public testing::Test
69 {
70  std::string snd, got, testgot;
72  UnixAddr server_addr, client_addr;
73  scc::util::Event startev, quitev;
74 
75  unix_networking() : snd("this is a test line\nthis is the second\nanother\n\nlast one")
76  {
77  Logger log;
78  log.add_cout();
79  log.id("test");
80 
81  std::system_error err;
82  scc::util::Filesystem::remove(server_addrname, &err);
83  server_addr.host(server_addrname);
84  scc::util::Filesystem::remove(client_addrname, &err);
85  client_addr.host(client_addrname);
86  }
87  virtual ~unix_networking()
88  {
89  std::system_error err;
90  scc::util::Filesystem::remove(server_addrname, &err);
91  scc::util::Filesystem::remove(client_addrname, &err);
92  }
93 
94  void tcp_stream_test(UnixTcpSock& sock)
95  {
96  Logger log;
97  log.add_cout();
98  log.id("unix tcp stream client");
99 
100  IoStream stream(sock, sock);
101  stringstream testst(snd);
102 
103  // first send and recieve using getline
104 
105  log << "sending " << snd.size() << endl;
106  ASSERT_TRUE(stream << snd << endl);
107  stream.flush();
108 
109  while (std::getline(testst, testgot))
110  {
111  ASSERT_TRUE(std::getline(stream, got));
112  log << "got reply " << got.size() << endl;
113  ASSERT_EQ(got, testgot);
114  }
115 
116  // now send and receive using stream operators
117 
118  ASSERT_TRUE(stream << snd << endl);
119  stream.flush();
120 
121  stringstream testst2(snd);
122  while (testst2 >> testgot)
123  {
124  ASSERT_TRUE(stream >> got);
125  log << "got reply " << got.size() << endl;
126  ASSERT_EQ(got, testgot);
127  }
128 
129  log << "tcp_stream_test: end" << endl;
130  }
131 
132  void tcp_writeread_test(UnixTcpSock& sock)
133  {
134  Logger log;
135  log.add_cout();
136  log.id("unix tcp readwrite client");
137 
138  log << "sending " << snd.size() << endl;
139  ASSERT_EQ(sock.send(snd.data(), snd.size()), snd.size());
140 
141  char buf[snd.size()];
142  size_t sz = 0, got;
143  do
144  {
145  got = sock.recv(buf+sz, snd.size()-sz);
146  log << "got reply " << got << endl;
147  ASSERT_GT(got, 0);
148  sz += got;
149  }
150  while (sz < snd.size());
151  ASSERT_EQ(sz, snd.size());
152  ASSERT_EQ(memcmp(buf, snd.data(), sz), 0);
153 
154  log << "end" << endl;
155  }
156 
157  void udp_writeread_test(UnixUdpSock& sock, UnixAddr& addr)
158  {
159  Logger log;
160  log.add_cout();
161  log.id("unix udp client");
162 
163  log << "sending " << snd.size() << " to " << addr.str() << endl;
164  ASSERT_EQ(sock.send(snd.data(), snd.size(), addr), snd.size());
165 
166  char buf[snd.size()];
167  size_t got;
168  UnixAddr from;
169  got = sock.recv(buf, snd.size(), from);
170  log << "got reply " << got << " from " << from.host() << endl;
171  ASSERT_EQ(got, snd.size());
172  ASSERT_EQ(memcmp(buf, snd.data(), snd.size()), 0);
173 
174  log << "end" << endl;
175  }
176 
177  static int udp_readwrite_server(Event& startev, UnixUdpSock& sock, UnixAddr& addr, UnixAddr& from, Event& quitev)
178  {
179  Logger log;
180  log.add_cout();
181  log.id("unix udp server");
182 
183  try
184  {
185  log << "reuse_addr" << endl;
186  sock.reuse_addr(true);
187  log << "bind to " << addr.host() << endl;
188  sock.bind(addr);
189 
190  startev.write(1); // signal the other thread it can start
191 
192  // now poll to see when we have new data (or are signalled to quit)
193  Poller pin;
194  pin.set(sock.fd(), Poller::input);
195  pin.set(quitev.fd(), Poller::input);
196 
197  while (1)
198  {
199  pin.wait();
200  if (pin.event(quitev.fd()))
201  {
202  startev.write(1); // no blocking allowed
203  return 0;
204  }
205  else if (pin.event(sock.fd()))
206  {
207  auto sz = sock.recv_next(); // how many bytes are waiting?
208  char buf[sz];
209  sock.recv(buf, sz, from);
210  log << "got " << sz << " from " << from.host() << endl;
211  sock.send(buf, sz, from);
212  }
213  }
214  }
215  catch (const std::exception& e)
216  {
217  log << "exception: " << e.what() << endl;
218  startev.write(1); // no blocking allowed
219  return errno;
220  }
221  startev.write(1); // no blocking allowed
222  return 0;
223  }
224 
225  static int tcp_iostream_server(Event& startev, UnixTcpSock& sock, UnixAddr& addr, UnixAddr& from)
226  {
227  Logger log;
228  log.add_cout();
229  log.id("unix tcp stream server");
230 
231  try
232  {
233  log << "reuse_addr" << endl;
234  sock.reuse_addr(true);
235  log << "bind to " << addr.host() << endl;
236  sock.bind(addr);
237  log << "listen" << endl;
238  sock.listen();
239 
240  startev.write(1); // signal the other thread it can start
241 
242  auto conn = sock.accept(from);
243  log << "connect from " << from << endl;
244 
245  IoStream stream(conn, conn);
246 
247  for (string got; std::getline(stream, got);) // loop until eof (socket closed)
248  {
249  log << "got " << got.size() << endl;
250  stream << got << endl;
251  }
252 
253  }
254  catch (const std::exception& e)
255  {
256  log << "exception: " << e.what() << endl;
257  startev.write(1); // no blocking allowed
258  return errno;
259  }
260  startev.write(1); // no blocking allowed
261  return 0;
262  }
263 
264  static int tcp_readwrite_server(Event& startev, UnixTcpSock& sock, UnixAddr& addr, UnixAddr& from)
265  {
266  Logger log;
267  log.add_cout();
268  log.id("unix tcp readwrite server");
269 
270  try
271  {
272  log << "reuse_addr" << endl;
273  sock.reuse_addr(true);
274  log << "bind to " << addr.host() << endl;
275  sock.bind(addr);
276  log << "listen" << endl;
277  sock.listen();
278 
279  startev.write(1); // signal the other thread it can start
280 
281  auto conn = sock.accept(from);
282  log << "connect from " << from << endl;
283 
284  char buf[16];
285  int sz;
286  while ((sz = conn.recv(buf, 16)) > 0) // loop until we get 0 (socket closed)
287  {
288  log << "got " << sz << endl;
289  conn.send(buf, sz);
290  }
291  }
292  catch (const std::exception& e)
293  {
294  log << "exception: " << e.what() << endl;
295  startev.write(1); // make sure we don't block
296  return errno;
297  }
298  startev.write(1);
299  return 0;
300  }
301 };
302 
303 TEST_F(unix_networking, addrnames)
304 {
305  ASSERT_EQ(server_addr.host(), server_addrname);
306  ASSERT_EQ(client_addr.host(), client_addrname);
307 }
308 
309 TEST_F(unix_networking, udp_readwrite)
310 {
311  UnixUdpSock servsock;
312  UnixAddr from;
313 
314  auto fut = async(udp_readwrite_server, std::ref(startev), std::ref(servsock), std::ref(server_addr), std::ref(from), std::ref(quitev));
315 
316  startev.read(); // wait for the server to listen
317 
319 
320  // bind to receive replies
321  log << "binding to " << client_addr << endl;
322  sock.bind(client_addr);
323 
324  udp_writeread_test(sock, server_addr);
325 
326  quitev.write(1); // signal server to quit (we don't have a connection)
327 
328  int res = fut.get();
329  log << "return code: " << res << endl;
330 
331  ASSERT_EQ(res, 0);
332  ASSERT_EQ(from.host(), client_addr.host());
333 }
334 
335 TEST_F(unix_networking, tcp_iostream)
336 {
337  UnixTcpSock servsock;
338 
339  UnixAddr from;
340  auto fut = async(tcp_iostream_server, std::ref(startev), std::ref(servsock), std::ref(server_addr), std::ref(from));
341 
342  startev.read();
343 
345 
346  // binding will allow the server to see our "address"; it is optional for connected protocol
347  log << "binding to " << client_addr << endl;
348  sock.bind(client_addr);
349 
350  log << "connect" << endl;
351  sock.connect(server_addr);
352 
353  log << "connected to addr name=" << server_addr.host() << endl;
354 
355  tcp_stream_test(sock);
356 
357  sock.close(); // close the socket to shut down the remote server connection
358 
359  int res = fut.get();
360  log << "return code: " << res << endl;
361 
362  ASSERT_EQ(res, 0);
363  ASSERT_EQ(from.host(), client_addr.host());
364 }
365 
366 TEST_F(unix_networking, tcp_readwrite)
367 {
368  UnixTcpSock servsock;
369  UnixAddr from;
370 
371  auto fut = async(tcp_readwrite_server, std::ref(startev), std::ref(servsock), std::ref(server_addr), std::ref(from));
372 
373  startev.read();
374 
376 
377  log << "binding to " << client_addr << endl;
378  sock.bind(client_addr);
379 
380  log << "connect" << endl;
381  sock.connect(server_addr);
382 
383  log << "connected to addr name=" << server_addr.host() << endl;
384 
385  tcp_writeread_test(sock);
386 
387  sock.close();
388  int res = fut.get();
389  log << "return code: " << res << endl;
390  ASSERT_EQ(res, 0);
391  ASSERT_EQ(from.host(), client_addr.host());
392 }
A unix domain address, which is a file of type "socket.".
Definition: unix.h:59
virtual std::string host() const
Get host name.
Definition: unix.cc:136
virtual std::string str() const
Descriptive string for socket address.
Definition: unix.cc:146
Unix domain tcp (stream) socket.
Definition: unix.h:97
Unix domain udp (datagram) socket.
Definition: unix.h:134
Signaling kernel event counter.
Definition: event.h:79
uint64_t read()
Read from (decrement) the event counter.
Definition: event.cc:82
int fd() const
Return file descriptor.
Definition: event.h:122
void write(uint64_t)
Write to (increment) the event counter.
Definition: event.cc:94
static void remove(const std::string &, std::system_error *=nullptr)
Remove the file or directory.
Definition: fs.cc:233
Input/output stream wrapper for reader/writer.
Definition: iostream.h:157
Thread-safe stream logger.
Definition: logger.h:86
void id(const std::string &id="")
Set the id.
Definition: logger.cc:425
void add_cout()
Add std::cout console stream.
Definition: logger.cc:418
Poller which allows polling of generic file descriptors for various events.
Definition: poller.h:66
void set(int, int)
Add a file desriptor to poller.
Definition: poller.cc:54
int event(int)
Return flags which were polled for this file descriptor.
Definition: poller.cc:171
Signaling kernel event counter.
Common file system utilities.
Internet tcp and udp networking.
Base input/output stream classes.
Thread safe logging.
Linux kernel i/o event notification (poller).
Unix domain tcp and udp networking.