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.sys.socket.UnixSocket; 28 29 import ocean.transition; 30 import ocean.stdc.posix.sys.socket; 31 import ocean.stdc.posix.sys.un; 32 import ocean.stdc.posix.sys.wait; 33 import core.sys.posix.unistd; 34 import ocean.stdc.posix.stdlib : mkdtemp; 35 import core.stdc.stdio; 36 import ocean.math.Math; 37 import ocean.core.Time; 38 import ocean.stdc.string; 39 import core.stdc.errno; 40 import core.thread; 41 42 import ocean.text.util.StringC; 43 44 static immutable istring CLIENT_STRING = "Hello from the client"; 45 46 static immutable istring SERVER_STRING = "Hello from the server"; 47 48 int runClient ( sockaddr_un* socket_address ) 49 { 50 auto client = new UnixSocket(); 51 52 scope (exit) client.close(); 53 54 auto socket_fd = client.socket(); 55 56 enforce(socket_fd >= 0, "socket() call failed!"); 57 58 auto connect_result = client.connect(socket_address); 59 60 enforce(connect_result == 0, 61 "connect() call failed"); 62 63 // send some data 64 client.write(CLIENT_STRING); 65 66 auto read_buffer = new char[max(SERVER_STRING.length, 67 CLIENT_STRING.length) + 1]; 68 read_buffer[] = 0; 69 70 auto buff = cast(void[])read_buffer; 71 72 // receive some data 73 auto read_bytes = client.recv(buff, 0); 74 75 enforce(read_bytes > 0); 76 77 read_buffer.length = read_bytes; 78 79 test(read_buffer == SERVER_STRING); 80 81 return 0; 82 } 83 84 version(UnitTest) {} else 85 int main ( ) 86 { 87 bool in_child = false; 88 89 auto path = mkdtemp("/tmp/Dunittest-XXXXXX\0".dup.ptr); 90 enforce(path !is null); 91 92 auto test_dir = StringC.toDString(path); 93 94 scope (exit) 95 { 96 if (!in_child) 97 { 98 auto r = rmdir(test_dir.ptr); 99 assert(r == 0, "Couldn't remove the temporary directory " ~ 100 test_dir ~ ": " ~ StringC.toDString(strerror(errno))); 101 } 102 } 103 104 auto socket_path = test_dir ~ "/socket"; 105 106 auto socket_address = sockaddr_un.create(socket_path); 107 108 auto server = new UnixSocket(); 109 110 // close the socket 111 scope (exit) server.close(); 112 113 auto socket_fd = server.socket(); 114 enforce(socket_fd >= 0, "socket() call failed!"); 115 116 auto bind_result = server.bind(&socket_address); 117 enforce(bind_result == 0, "bind() call failed!"); 118 119 int backlog = 10; 120 121 auto listen_result = server.listen(backlog); 122 enforce(listen_result == 0, "listen() call failed!"); 123 124 // Since the process already called bind and listen, 125 // it is safe to connect from a child process. 126 // NOTE: even though child process inherits the listening socket, 127 // unless it `accept`s, no connect() attempts will be redirected to it 128 pid_t pid = fork(); 129 130 enforce(pid != -1); 131 132 if (pid == 0) // client 133 { 134 in_child = true; 135 return runClient(&socket_address); 136 } 137 138 scope (exit) 139 { 140 auto r = unlink(socket_path.ptr); 141 assert(r == 0, "Couldn't remove the socket file " ~ socket_path ~ 142 ": " ~ StringC.toDString(strerror(errno))); 143 } 144 145 int connection_fd; 146 147 auto peer_socket = new UnixSocket(); 148 149 scope (exit) peer_socket.close(); 150 151 if (peer_socket.accept(server) != -1) 152 { 153 connection_handler(peer_socket); 154 } 155 156 int status; 157 158 waitpid(pid, &status, 0); 159 160 enforce(status == 0, "Child exit status should be 0"); 161 162 return 0; 163 } 164 165 void connection_handler ( UnixSocket peer_socket ) 166 { 167 auto read_buffer = new char[max(SERVER_STRING.length, 168 CLIENT_STRING.length) + 1]; 169 read_buffer[] = '\0'; 170 171 auto buff = cast(void[])read_buffer; 172 173 auto read_bytes = peer_socket.recv(buff, 0); 174 175 enforce(read_bytes > 1); 176 177 read_buffer.length = read_bytes; 178 179 enforce(read_buffer == CLIENT_STRING, 180 cast(istring) ("Expected: " ~ CLIENT_STRING ~ " Got: " ~ read_buffer)); 181 182 // send the response 183 peer_socket.write(SERVER_STRING); 184 }