prselect.c (10181B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /*********************************************************************** 7 ** 1997 - Netscape Communications Corporation 8 ** 9 ** Name: prselect_err.c 10 ** 11 ** Description: tests PR_Select with sockets Error condition functions. 12 ** 13 ** Modification History: 14 ** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. 15 ** The debug mode will print all of the printfs associated with this 16 *test. 17 ** The regress mode will be the default mode. Since the regress tool 18 *limits 19 ** the output to a one line status:PASS or FAIL,all of the printf 20 *statements 21 ** have been handled with an if (debug_mode) statement. 22 ***********************************************************************/ 23 24 /*********************************************************************** 25 ** Includes 26 ***********************************************************************/ 27 /* Used to get the command line option */ 28 #include "plgetopt.h" 29 #include "prttools.h" 30 31 #include "prinit.h" 32 #include "prio.h" 33 #include "prlog.h" 34 #include "prprf.h" 35 #include "prerror.h" 36 #include "prnetdb.h" 37 38 #include <stdio.h> 39 #include <string.h> 40 #include <stdlib.h> 41 42 /*********************************************************************** 43 ** PRIVATE FUNCTION: Test_Result 44 ** DESCRIPTION: Used in conjunction with the regress tool, prints out the 45 ** status of the test case. 46 ** INPUTS: PASS/FAIL 47 ** OUTPUTS: None 48 ** RETURN: None 49 ** SIDE EFFECTS: 50 ** 51 ** RESTRICTIONS: 52 ** None 53 ** MEMORY: NA 54 ** ALGORITHM: Determine what the status is and print accordingly. 55 ** 56 ***********************************************************************/ 57 58 static Test_Result(int result) { 59 if (result == PASS) { 60 printf("PASS\n"); 61 } else { 62 printf("FAIL\n"); 63 } 64 } 65 66 static void clientThreadFunc(void* arg) { 67 PRUint16 port = (PRUint16)arg; 68 PRFileDesc* sock; 69 PRNetAddr addr; 70 char buf[128]; 71 int i; 72 73 addr.inet.family = AF_INET; 74 addr.inet.port = PR_htons(port); 75 addr.inet.ip = PR_htonl(INADDR_LOOPBACK); 76 PR_snprintf(buf, sizeof(buf), "%hu", port); 77 78 for (i = 0; i < 5; i++) { 79 sock = PR_NewTCPSocket(); 80 PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT); 81 PR_Write(sock, buf, sizeof(buf)); 82 PR_Close(sock); 83 } 84 } 85 86 int main(int argc, char** argv) { 87 PRFileDesc *listenSock1, *listenSock2; 88 PRFileDesc* badFD; 89 PRFileDesc *fds0[10], *fds1[10], **fds, **other_fds; 90 PRIntn nfds; 91 PRUint16 listenPort1, listenPort2; 92 PRNetAddr addr; 93 PR_fd_set readFdSet; 94 char buf[128]; 95 PRThread* clientThread; 96 PRInt32 retVal; 97 PRIntn i, j; 98 99 /* The command line argument: -d is used to determine if the test is being run 100 in debug mode. The regress tool requires only one line output:PASS or FAIL. 101 All of the printfs associated with this test has been handled with a if 102 (debug_mode) test. Usage: test_name -d 103 */ 104 PLOptStatus os; 105 PLOptState* opt = PL_CreateOptState(argc, argv, "d:"); 106 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) { 107 if (PL_OPT_BAD == os) { 108 continue; 109 } 110 switch (opt->option) { 111 case 'd': /* debug mode */ 112 debug_mode = 1; 113 break; 114 default: 115 break; 116 } 117 } 118 PL_DestroyOptState(opt); 119 120 /* main test */ 121 122 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); 123 124 if (debug_mode) { 125 printf("This program tests PR_Select with sockets. Timeout, error\n"); 126 printf("reporting, and normal operation are tested.\n\n"); 127 } 128 129 /* Create two listening sockets */ 130 if ((listenSock1 = PR_NewTCPSocket()) == NULL) { 131 fprintf(stderr, "Can't create a new TCP socket\n"); 132 if (!debug_mode) { 133 Test_Result(FAIL); 134 } 135 exit(1); 136 } 137 addr.inet.family = AF_INET; 138 addr.inet.ip = PR_htonl(INADDR_ANY); 139 addr.inet.port = PR_htons(0); 140 if (PR_Bind(listenSock1, &addr) == PR_FAILURE) { 141 fprintf(stderr, "Can't bind socket\n"); 142 if (!debug_mode) { 143 Test_Result(FAIL); 144 } 145 exit(1); 146 } 147 if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) { 148 fprintf(stderr, "PR_GetSockName failed\n"); 149 if (!debug_mode) { 150 Test_Result(FAIL); 151 } 152 exit(1); 153 } 154 listenPort1 = PR_ntohs(addr.inet.port); 155 if (PR_Listen(listenSock1, 5) == PR_FAILURE) { 156 fprintf(stderr, "Can't listen on a socket\n"); 157 if (!debug_mode) { 158 Test_Result(FAIL); 159 } 160 exit(1); 161 } 162 163 if ((listenSock2 = PR_NewTCPSocket()) == NULL) { 164 fprintf(stderr, "Can't create a new TCP socket\n"); 165 if (!debug_mode) { 166 Test_Result(FAIL); 167 } 168 exit(1); 169 } 170 addr.inet.family = AF_INET; 171 addr.inet.ip = PR_htonl(INADDR_ANY); 172 addr.inet.port = PR_htons(0); 173 if (PR_Bind(listenSock2, &addr) == PR_FAILURE) { 174 fprintf(stderr, "Can't bind socket\n"); 175 if (!debug_mode) { 176 Test_Result(FAIL); 177 } 178 exit(1); 179 } 180 if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) { 181 fprintf(stderr, "PR_GetSockName failed\n"); 182 if (!debug_mode) { 183 Test_Result(FAIL); 184 } 185 exit(1); 186 } 187 listenPort2 = PR_ntohs(addr.inet.port); 188 if (PR_Listen(listenSock2, 5) == PR_FAILURE) { 189 fprintf(stderr, "Can't listen on a socket\n"); 190 if (!debug_mode) { 191 Test_Result(FAIL); 192 } 193 exit(1); 194 } 195 PR_snprintf(buf, sizeof(buf), 196 "The server thread is listening on ports %hu and %hu\n\n", 197 listenPort1, listenPort2); 198 printf("%s", buf); 199 200 /* Set up the fd set */ 201 PR_FD_ZERO(&readFdSet); 202 PR_FD_SET(listenSock1, &readFdSet); 203 PR_FD_SET(listenSock2, &readFdSet); 204 205 /* Testing timeout */ 206 if (debug_mode) { 207 printf("PR_Select should time out in 5 seconds\n"); 208 } 209 retVal = PR_Select(0 /* unused */, &readFdSet, NULL, NULL, 210 PR_SecondsToInterval(5)); 211 if (retVal != 0) { 212 PR_snprintf(buf, sizeof(buf), 213 "PR_Select should time out and return 0, but it returns %ld\n", 214 retVal); 215 fprintf(stderr, "%s", buf); 216 if (retVal == -1) { 217 fprintf(stderr, "Error %d, oserror %d\n", PR_GetError(), PR_GetOSError()); 218 if (!debug_mode) { 219 Test_Result(FAIL); 220 } 221 } 222 exit(1); 223 } 224 if (debug_mode) { 225 printf("PR_Select timed out. Test passed.\n\n"); 226 } else { 227 Test_Result(PASS); 228 } 229 230 /* Testing bad fd */ 231 printf("PR_Select should detect a bad file descriptor\n"); 232 if ((badFD = PR_NewTCPSocket()) == NULL) { 233 fprintf(stderr, "Can't create a TCP socket\n"); 234 exit(1); 235 } 236 237 PR_FD_SET(listenSock1, &readFdSet); 238 PR_FD_SET(listenSock2, &readFdSet); 239 PR_FD_SET(badFD, &readFdSet); 240 PR_Close(badFD); /* make the fd bad */ 241 retVal = 242 PR_Select(0 /* unused */, &readFdSet, NULL, NULL, PR_INTERVAL_NO_TIMEOUT); 243 if (retVal != -1 || PR_GetError() != PR_BAD_DESCRIPTOR_ERROR) { 244 fprintf(stderr, 245 "Failed to detect the bad fd: " 246 "PR_Select returns %d\n", 247 retVal); 248 if (retVal == -1) { 249 fprintf(stderr, "Error %d, oserror %d\n", PR_GetError(), PR_GetOSError()); 250 } 251 exit(1); 252 } 253 printf("PR_Select detected a bad fd. Test passed.\n\n"); 254 PR_FD_CLR(badFD, &readFdSet); 255 256 clientThread = PR_CreateThread(PR_USER_THREAD, clientThreadFunc, 257 (void*)listenPort1, PR_PRIORITY_NORMAL, 258 PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD, 0); 259 if (clientThread == NULL) { 260 fprintf(stderr, "can't create thread\n"); 261 exit(1); 262 } 263 264 clientThread = PR_CreateThread(PR_USER_THREAD, clientThreadFunc, 265 (void*)listenPort2, PR_PRIORITY_NORMAL, 266 PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD, 0); 267 if (clientThread == NULL) { 268 fprintf(stderr, "can't create thread\n"); 269 exit(1); 270 } 271 272 printf("Two client threads are created. Each of them will\n"); 273 printf("send data to one of the two ports the server is listening on.\n"); 274 printf("The data they send is the port number. Each of them send\n"); 275 printf("the data five times, so you should see ten lines below,\n"); 276 printf("interleaved in an arbitrary order.\n"); 277 278 /* set up the fd array */ 279 fds = fds0; 280 other_fds = fds1; 281 fds[0] = listenSock1; 282 fds[1] = listenSock2; 283 nfds = 2; 284 PR_FD_SET(listenSock1, &readFdSet); 285 PR_FD_SET(listenSock2, &readFdSet); 286 287 /* 20 events total */ 288 i = 0; 289 while (i < 20) { 290 PRFileDesc** tmp; 291 int nextIndex; 292 int nEvents = 0; 293 294 retVal = PR_Select(0 /* unused */, &readFdSet, NULL, NULL, 295 PR_INTERVAL_NO_TIMEOUT); 296 PR_ASSERT(retVal != 0); /* no timeout */ 297 if (retVal == -1) { 298 fprintf(stderr, "PR_Select failed (%d, %d)\n", PR_GetError(), 299 PR_GetOSError()); 300 exit(1); 301 } 302 303 nextIndex = 2; 304 /* the two listening sockets */ 305 for (j = 0; j < 2; j++) { 306 other_fds[j] = fds[j]; 307 if (PR_FD_ISSET(fds[j], &readFdSet)) { 308 PRFileDesc* sock; 309 310 nEvents++; 311 sock = PR_Accept(fds[j], NULL, PR_INTERVAL_NO_TIMEOUT); 312 if (sock == NULL) { 313 fprintf(stderr, "PR_Accept() failed\n"); 314 exit(1); 315 } 316 other_fds[nextIndex] = sock; 317 PR_FD_SET(sock, &readFdSet); 318 nextIndex++; 319 } 320 PR_FD_SET(fds[j], &readFdSet); 321 } 322 323 for (j = 2; j < nfds; j++) { 324 if (PR_FD_ISSET(fds[j], &readFdSet)) { 325 PRInt32 nBytes; 326 327 PR_FD_CLR(fds[j], &readFdSet); 328 nEvents++; 329 nBytes = PR_Read(fds[j], buf, sizeof(buf)); 330 if (nBytes == -1) { 331 fprintf(stderr, "PR_Read() failed\n"); 332 exit(1); 333 } 334 /* Just to be safe */ 335 buf[127] = '\0'; 336 PR_Close(fds[j]); 337 printf("The server received \"%s\" from a client\n", buf); 338 } else { 339 PR_FD_SET(fds[j], &readFdSet); 340 other_fds[nextIndex] = fds[j]; 341 nextIndex++; 342 } 343 } 344 345 PR_ASSERT(retVal == nEvents); 346 /* swap */ 347 tmp = fds; 348 fds = other_fds; 349 other_fds = tmp; 350 nfds = nextIndex; 351 i += nEvents; 352 } 353 354 printf("All tests finished\n"); 355 PR_Cleanup(); 356 return 0; 357 }