1 /******************************************************************************* 2 3 Test-suite for UnixSockets. 4 5 The tests involve unix sockets and forking 6 processes, so are placed in this slowtest module. 7 8 FLAKY: the unittests in this module are very flaky, as they rely on making 9 various system calls (fork(), waitpid(), epoll_wait(), epoll_ctl(), etc) 10 which could, under certain environmental conditions, fail. 11 12 Copyright: 13 Copyright (c) 2015-2017 dunnhumby Germany GmbH. 14 All rights reserved. 15 16 License: 17 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 18 Alternatively, this file may be distributed under the terms of the Tango 19 3-Clause BSD License (see LICENSE_BSD.txt for details). 20 21 *******************************************************************************/ 22 23 module integrationtest.unixsocket.main; 24 25 import ocean.core.Enforce; 26 import ocean.core.Test; 27 import ocean.math.Math; 28 import ocean.stdc.posix.sys.un; 29 import ocean.sys.socket.UnixSocket; 30 import ocean.text.util.StringC; 31 import ocean.meta.types.Qualifiers; 32 33 import core.stdc.errno; 34 import core.stdc.stdio; 35 import core.stdc..string; 36 import core.sys.posix.stdlib : mkdtemp; 37 import core.sys.posix.unistd; 38 import core.sys.posix.sys.wait; 39 import core.thread; 40 41 static immutable istring CLIENT_STRING = "Hello from the client"; 42 43 static immutable istring SERVER_STRING = "Hello from the server"; 44 45 int runClient ( sockaddr_un* socket_address ) 46 { 47 auto client = new UnixSocket(); 48 49 scope (exit) client.close(); 50 51 auto socket_fd = client.socket(); 52 53 enforce(socket_fd >= 0, "socket() call failed!"); 54 55 auto connect_result = client.connect(socket_address); 56 57 enforce(connect_result == 0, 58 "connect() call failed"); 59 60 // send some data 61 client.write(CLIENT_STRING); 62 63 auto read_buffer = new char[max(SERVER_STRING.length, 64 CLIENT_STRING.length) + 1]; 65 read_buffer[] = 0; 66 67 auto buff = cast(void[])read_buffer; 68 69 // receive some data 70 auto read_bytes = client.recv(buff, 0); 71 72 enforce(read_bytes > 0); 73 74 read_buffer.length = read_bytes; 75 76 test(read_buffer == SERVER_STRING); 77 78 return 0; 79 } 80 81 version (unittest) {} else 82 int main ( ) 83 { 84 bool in_child = false; 85 86 auto path = mkdtemp("/tmp/Dunittest-XXXXXX\0".dup.ptr); 87 enforce(path !is null); 88 89 auto test_dir = StringC.toDString(path); 90 91 scope (exit) 92 { 93 if (!in_child) 94 { 95 auto r = rmdir(test_dir.ptr); 96 assert(r == 0, "Couldn't remove the temporary directory " ~ 97 test_dir ~ ": " ~ StringC.toDString(strerror(errno))); 98 } 99 } 100 101 auto socket_path = test_dir ~ "/socket"; 102 103 auto socket_address = sockaddr_un.create(socket_path); 104 105 auto server = new UnixSocket(); 106 107 // close the socket 108 scope (exit) server.close(); 109 110 auto socket_fd = server.socket(); 111 enforce(socket_fd >= 0, "socket() call failed!"); 112 113 auto bind_result = server.bind(&socket_address); 114 enforce(bind_result == 0, "bind() call failed!"); 115 116 int backlog = 10; 117 118 auto listen_result = server.listen(backlog); 119 enforce(listen_result == 0, "listen() call failed!"); 120 121 // Since the process already called bind and listen, 122 // it is safe to connect from a child process. 123 // NOTE: even though child process inherits the listening socket, 124 // unless it `accept`s, no connect() attempts will be redirected to it 125 pid_t pid = fork(); 126 127 enforce(pid != -1); 128 129 if (pid == 0) // client 130 { 131 in_child = true; 132 return runClient(&socket_address); 133 } 134 135 scope (exit) 136 { 137 auto r = unlink(socket_path.ptr); 138 assert(r == 0, "Couldn't remove the socket file " ~ socket_path ~ 139 ": " ~ StringC.toDString(strerror(errno))); 140 } 141 142 int connection_fd; 143 144 auto peer_socket = new UnixSocket(); 145 146 scope (exit) peer_socket.close(); 147 148 if (peer_socket.accept(server) != -1) 149 { 150 connection_handler(peer_socket); 151 } 152 153 int status; 154 155 waitpid(pid, &status, 0); 156 157 enforce(status == 0, "Child exit status should be 0"); 158 159 return 0; 160 } 161 162 void connection_handler ( UnixSocket peer_socket ) 163 { 164 auto read_buffer = new char[max(SERVER_STRING.length, 165 CLIENT_STRING.length) + 1]; 166 read_buffer[] = '\0'; 167 168 auto buff = cast(void[])read_buffer; 169 170 auto read_bytes = peer_socket.recv(buff, 0); 171 172 enforce(read_bytes > 1); 173 174 read_buffer.length = read_bytes; 175 176 enforce(read_buffer == CLIENT_STRING, 177 cast(istring) ("Expected: " ~ CLIENT_STRING ~ " Got: " ~ read_buffer)); 178 179 // send the response 180 peer_socket.write(SERVER_STRING); 181 }