nspr: import 3.0 RC1 cutoff from CVS
[mozilla-nspr.git] / nsprpub / pr / tests / nbconn.c
blob099150a516498cc0d88484b55cb3842987596204
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is the Netscape Portable Runtime (NSPR).
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998-2000
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
39 * A test for nonblocking connect. Functions tested include PR_Connect,
40 * PR_Poll, and PR_GetConnectStatus.
42 * The test should be invoked with a host name, for example:
43 * nbconn www.netscape.com
44 * It will do a nonblocking connect to port 80 (HTTP) on that host,
45 * and when connected, issue the "GET /" HTTP command.
47 * You should run this test in three ways:
48 * 1. To a known web site, such as www.netscape.com. The HTML of the
49 * top-level page at the web site should be printed.
50 * 2. To a machine not running a web server at port 80. This test should
51 * fail. Ideally the error code should be PR_CONNECT_REFUSED_ERROR.
52 * But it is possible to return PR_UNKNOWN_ERROR on certain platforms.
53 * 3. To an unreachable machine, for example, a machine that is off line.
54 * The test should fail after the connect times out. Ideally the
55 * error code should be PR_IO_TIMEOUT_ERROR, but it is possible to
56 * return PR_UNKNOWN_ERROR on certain platforms.
59 #include "nspr.h"
60 #include "plgetopt.h"
61 #include <stdio.h>
62 #include <string.h>
64 #ifdef XP_MAC
65 #define printf PR_LogPrint
66 extern void SetupMacPrintfLog(char *logFile);
67 static char *hosts[4] = {"cynic", "warp", "gandalf", "neon"};
68 #endif
70 #define SERVER_MAX_BIND_COUNT 100
71 #define DATA_BUF_SIZE 256
72 #define TCP_SERVER_PORT 10000
73 #define TCP_UNUSED_PORT 211
75 typedef struct Server_Param {
76 PRFileDesc *sp_fd; /* server port */
77 } Server_Param;
78 static void PR_CALLBACK TCP_Server(void *arg);
80 int _debug_on;
81 #define DPRINTF(arg) if (_debug_on) printf arg
83 static PRIntn connection_success_test();
84 static PRIntn connection_failure_test();
86 int main(int argc, char **argv)
88 PRHostEnt he;
89 char buf[1024];
90 PRNetAddr addr;
91 PRPollDesc pd;
92 PRStatus rv;
93 PRSocketOptionData optData;
94 const char *hostname = NULL;
95 PRIntn default_case, n, bytes_read, bytes_sent;
96 PRInt32 failed_already = 0;
97 #ifdef XP_MAC
98 int index;
99 PRIntervalTime timeout;
100 #endif
103 * -d debug mode
106 PLOptStatus os;
107 PLOptState *opt = PL_CreateOptState(argc, argv, "d");
108 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
110 if (PL_OPT_BAD == os) continue;
111 switch (opt->option)
113 case 0: /* debug mode */
114 hostname = opt->value;
115 break;
116 case 'd': /* debug mode */
117 _debug_on = 1;
118 break;
119 default:
120 break;
123 PL_DestroyOptState(opt);
125 #ifdef XP_MAC
126 SetupMacPrintfLog("nbconn.log");
127 for (index=0; index<4; index++) {
128 argv[1] = hosts[index];
129 timeout = PR_INTERVAL_NO_TIMEOUT;
130 if (index == 3)
131 timeout = PR_SecondsToInterval(10UL);
132 #endif
135 PR_STDIO_INIT();
136 #ifndef XP_MAC
137 if (hostname)
138 default_case = 0;
139 else
140 default_case = 1;
141 #endif
143 if (default_case) {
146 * In the default case the following tests are executed:
147 * 1. successful connection: a server thread accepts a connection
148 * from the main thread
149 * 2. unsuccessful connection: the main thread tries to connect to a
150 * non-existent port and expects to get an error
152 rv = connection_success_test();
153 if (rv == 0)
154 rv = connection_failure_test();
155 return rv;
156 } else {
157 PRFileDesc *sock;
159 if (PR_GetHostByName(argv[1], buf, sizeof(buf), &he) == PR_FAILURE) {
160 printf( "Unknown host: %s\n", argv[1]);
161 exit(1);
162 } else {
163 printf( "host: %s\n", buf);
165 PR_EnumerateHostEnt(0, &he, 80, &addr);
167 sock = PR_NewTCPSocket();
168 optData.option = PR_SockOpt_Nonblocking;
169 optData.value.non_blocking = PR_TRUE;
170 PR_SetSocketOption(sock, &optData);
171 rv = PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT);
172 if (rv == PR_FAILURE && PR_GetError() == PR_IN_PROGRESS_ERROR) {
173 printf( "Connect in progress\n");
176 pd.fd = sock;
177 pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
178 #ifndef XP_MAC
179 n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
180 #else
181 n = PR_Poll(&pd, 1, timeout);
182 #endif
183 if (n == -1) {
184 printf( "PR_Poll failed\n");
185 exit(1);
187 printf( "PR_Poll returns %d\n", n);
188 if (pd.out_flags & PR_POLL_READ) {
189 printf( "PR_POLL_READ\n");
191 if (pd.out_flags & PR_POLL_WRITE) {
192 printf( "PR_POLL_WRITE\n");
194 if (pd.out_flags & PR_POLL_EXCEPT) {
195 printf( "PR_POLL_EXCEPT\n");
197 if (pd.out_flags & PR_POLL_ERR) {
198 printf( "PR_POLL_ERR\n");
200 if (pd.out_flags & PR_POLL_NVAL) {
201 printf( "PR_POLL_NVAL\n");
204 if (PR_GetConnectStatus(&pd) == PR_SUCCESS) {
205 printf("PR_GetConnectStatus: connect succeeded\n");
206 /* Mac and Win16 have trouble printing to the console. */
207 #if !defined(XP_MAC) && !defined(WIN16)
208 PR_Write(sock, "GET /\r\n\r\n", 9);
209 PR_Shutdown(sock, PR_SHUTDOWN_SEND);
210 pd.in_flags = PR_POLL_READ;
211 while (1) {
212 n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
213 printf( "poll returns %d\n", n);
214 n = PR_Read(sock, buf, sizeof(buf));
215 printf( "read returns %d\n", n);
216 if (n <= 0) {
217 break;
219 PR_Write(PR_STDOUT, buf, n);
221 #endif
222 } else {
223 if (PR_GetError() == PR_IN_PROGRESS_ERROR) {
224 printf( "PR_GetConnectStatus: connect still in progress\n");
225 exit(1);
227 printf( "PR_GetConnectStatus: connect failed: (%ld, %ld)\n",
228 PR_GetError(), PR_GetOSError());
230 PR_Close(sock);
231 #ifdef XP_MAC
232 } /* end of for loop */
233 #endif
234 printf( "PASS\n");
235 return 0;
242 * TCP Server
243 * Server Thread
244 * Accept a connection from the client and write some data
246 static void PR_CALLBACK
247 TCP_Server(void *arg)
249 Server_Param *sp = (Server_Param *) arg;
250 PRFileDesc *sockfd, *newsockfd;
251 char data_buf[DATA_BUF_SIZE];
252 PRIntn rv, bytes_read;
254 sockfd = sp->sp_fd;
255 if ((newsockfd = PR_Accept(sockfd, NULL,
256 PR_INTERVAL_NO_TIMEOUT)) == NULL) {
257 fprintf(stderr,"ERROR - PR_Accept failed: (%d,%d)\n",
258 PR_GetError(), PR_GetOSError());
259 return;
261 bytes_read = 0;
262 while (bytes_read != DATA_BUF_SIZE) {
263 rv = PR_Read(newsockfd, data_buf + bytes_read ,
264 DATA_BUF_SIZE - bytes_read);
265 if (rv < 0) {
266 fprintf(stderr,"Error - PR_Read failed: (%d, %d)\n",
267 PR_GetError(), PR_GetOSError());
268 PR_Close(newsockfd);
269 return;
271 PR_ASSERT(rv != 0);
272 bytes_read += rv;
274 DPRINTF(("Bytes read from client - %d\n",bytes_read));
275 rv = PR_Write(newsockfd, data_buf,DATA_BUF_SIZE);
276 if (rv < 0) {
277 fprintf(stderr,"Error - PR_Write failed: (%d, %d)\n",
278 PR_GetError(), PR_GetOSError());
279 PR_Close(newsockfd);
280 return;
282 PR_ASSERT(rv == DATA_BUF_SIZE);
283 DPRINTF(("Bytes written to client - %d\n",rv));
284 PR_Close(newsockfd);
289 * test for successful connection using a non-blocking socket
291 static PRIntn
292 connection_success_test()
294 PRFileDesc *sockfd = NULL, *conn_fd = NULL;
295 PRNetAddr netaddr;
296 PRInt32 i, rv;
297 PRPollDesc pd;
298 PRSocketOptionData optData;
299 PRThread *thr = NULL;
300 Server_Param sp;
301 char send_buf[DATA_BUF_SIZE], recv_buf[DATA_BUF_SIZE];
302 PRIntn default_case, n, bytes_read, bytes_sent;
303 PRIntn failed_already = 0;
306 * Create a tcp socket
308 if ((sockfd = PR_NewTCPSocket()) == NULL) {
309 fprintf(stderr,"Error - PR_NewTCPSocket failed\n");
310 failed_already=1;
311 goto def_exit;
313 memset(&netaddr, 0 , sizeof(netaddr));
314 netaddr.inet.family = PR_AF_INET;
315 netaddr.inet.port = PR_htons(TCP_SERVER_PORT);
316 netaddr.inet.ip = PR_htonl(PR_INADDR_ANY);
318 * try a few times to bind server's address, if addresses are in
319 * use
321 i = 0;
322 while (PR_Bind(sockfd, &netaddr) < 0) {
323 if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) {
324 netaddr.inet.port += 2;
325 if (i++ < SERVER_MAX_BIND_COUNT)
326 continue;
328 fprintf(stderr,"ERROR - PR_Bind failed: (%d,%d)\n",
329 PR_GetError(), PR_GetOSError());
330 failed_already=1;
331 goto def_exit;
334 if (PR_Listen(sockfd, 32) < 0) {
335 fprintf(stderr,"ERROR - PR_Listen failed: (%d,%d)\n",
336 PR_GetError(), PR_GetOSError());
337 failed_already=1;
338 goto def_exit;
341 if (PR_GetSockName(sockfd, &netaddr) < 0) {
342 fprintf(stderr,"ERROR - PR_GetSockName failed: (%d,%d)\n",
343 PR_GetError(), PR_GetOSError());
344 failed_already=1;
345 goto def_exit;
347 if ((conn_fd = PR_NewTCPSocket()) == NULL) {
348 fprintf(stderr,"Error - PR_NewTCPSocket failed\n");
349 failed_already=1;
350 goto def_exit;
352 optData.option = PR_SockOpt_Nonblocking;
353 optData.value.non_blocking = PR_TRUE;
354 PR_SetSocketOption(conn_fd, &optData);
355 rv = PR_Connect(conn_fd, &netaddr, PR_INTERVAL_NO_TIMEOUT);
356 if (rv == PR_FAILURE) {
357 if (PR_GetError() == PR_IN_PROGRESS_ERROR) {
358 DPRINTF(("Connect in progress\n"));
359 } else {
360 fprintf(stderr,"Error - PR_Connect failed: (%d, %d)\n",
361 PR_GetError(), PR_GetOSError());
362 failed_already=1;
363 goto def_exit;
367 * Now create a thread to accept a connection
369 sp.sp_fd = sockfd;
370 thr = PR_CreateThread(PR_USER_THREAD, TCP_Server, (void *)&sp,
371 PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
372 if (thr == NULL) {
373 fprintf(stderr,"Error - PR_CreateThread failed: (%d,%d)\n",
374 PR_GetError(), PR_GetOSError());
375 failed_already=1;
376 goto def_exit;
378 DPRINTF(("Created TCP_Server thread [0x%x]\n",thr));
379 pd.fd = conn_fd;
380 pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
381 #ifndef XP_MAC
382 n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
383 #else
384 n = PR_Poll(&pd, 1, timeout);
385 #endif
386 if (n == -1) {
387 fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
388 PR_GetError(), PR_GetOSError());
389 failed_already=1;
390 goto def_exit;
392 if (PR_GetConnectStatus(&pd) == PR_SUCCESS) {
393 PRInt32 rv;
395 DPRINTF(("Connection successful\n"));
398 * Write some data, read it back and check data integrity to
399 * make sure the connection is good
401 pd.in_flags = PR_POLL_WRITE;
402 bytes_sent = 0;
403 memset(send_buf, 'a', DATA_BUF_SIZE);
404 while (bytes_sent != DATA_BUF_SIZE) {
405 rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
406 if (rv < 0) {
407 fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
408 PR_GetError(), PR_GetOSError());
409 failed_already=1;
410 goto def_exit;
412 PR_ASSERT((rv == 1) && (pd.out_flags == PR_POLL_WRITE));
413 rv = PR_Write(conn_fd, send_buf + bytes_sent,
414 DATA_BUF_SIZE - bytes_sent);
415 if (rv < 0) {
416 fprintf(stderr,"Error - PR_Write failed: (%d, %d)\n",
417 PR_GetError(), PR_GetOSError());
418 failed_already=1;
419 goto def_exit;
421 PR_ASSERT(rv > 0);
422 bytes_sent += rv;
424 DPRINTF(("Bytes written to server - %d\n",bytes_sent));
425 PR_Shutdown(conn_fd, PR_SHUTDOWN_SEND);
426 pd.in_flags = PR_POLL_READ;
427 bytes_read = 0;
428 memset(recv_buf, 0, DATA_BUF_SIZE);
429 while (bytes_read != DATA_BUF_SIZE) {
430 rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
431 if (rv < 0) {
432 fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
433 PR_GetError(), PR_GetOSError());
434 failed_already=1;
435 goto def_exit;
437 PR_ASSERT((rv == 1) && (pd.out_flags == PR_POLL_READ));
438 rv = PR_Read(conn_fd, recv_buf + bytes_read ,
439 DATA_BUF_SIZE - bytes_read);
440 if (rv < 0) {
441 fprintf(stderr,"Error - PR_Read failed: (%d, %d)\n",
442 PR_GetError(), PR_GetOSError());
443 failed_already=1;
444 goto def_exit;
446 PR_ASSERT(rv != 0);
447 bytes_read += rv;
449 DPRINTF(("Bytes read from server - %d\n",bytes_read));
451 * verify the data read
453 if (memcmp(send_buf, recv_buf, DATA_BUF_SIZE) != 0) {
454 fprintf(stderr,"ERROR - data corruption\n");
455 failed_already=1;
456 goto def_exit;
458 DPRINTF(("Data integrity verified\n"));
459 } else {
460 fprintf(stderr,"PR_GetConnectStatus: connect failed: (%ld, %ld)\n",
461 PR_GetError(), PR_GetOSError());
462 failed_already = 1;
463 goto def_exit;
465 def_exit:
466 if (thr) {
467 PR_JoinThread(thr);
468 thr = NULL;
470 if (sockfd) {
471 PR_Close(sockfd);
472 sockfd = NULL;
474 if (conn_fd) {
475 PR_Close(conn_fd);
476 conn_fd = NULL;
478 if (failed_already)
479 return 1;
480 else
481 return 0;
486 * test for connection to a non-existent port using a non-blocking socket
488 static PRIntn
489 connection_failure_test()
491 PRFileDesc *sockfd = NULL, *conn_fd = NULL;
492 PRNetAddr netaddr;
493 PRInt32 i, rv;
494 PRPollDesc pd;
495 PRSocketOptionData optData;
496 PRIntn n, failed_already = 0;
499 * Create a tcp socket
501 if ((sockfd = PR_NewTCPSocket()) == NULL) {
502 fprintf(stderr,"Error - PR_NewTCPSocket failed\n");
503 failed_already=1;
504 goto def_exit;
506 memset(&netaddr, 0 , sizeof(netaddr));
507 netaddr.inet.family = PR_AF_INET;
508 netaddr.inet.port = PR_htons(TCP_SERVER_PORT);
509 netaddr.inet.ip = PR_htonl(PR_INADDR_ANY);
511 * try a few times to bind server's address, if addresses are in
512 * use
514 i = 0;
515 while (PR_Bind(sockfd, &netaddr) < 0) {
516 if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) {
517 netaddr.inet.port += 2;
518 if (i++ < SERVER_MAX_BIND_COUNT)
519 continue;
521 fprintf(stderr,"ERROR - PR_Bind failed: (%d,%d)\n",
522 PR_GetError(), PR_GetOSError());
523 failed_already=1;
524 goto def_exit;
527 if (PR_GetSockName(sockfd, &netaddr) < 0) {
528 fprintf(stderr,"ERROR - PR_GetSockName failed: (%d,%d)\n",
529 PR_GetError(), PR_GetOSError());
530 failed_already=1;
531 goto def_exit;
533 #ifdef AIX
535 * On AIX, set to unused/reserved port
537 netaddr.inet.port = PR_htons(TCP_UNUSED_PORT);
538 #endif
539 if ((conn_fd = PR_NewTCPSocket()) == NULL) {
540 fprintf(stderr,"Error - PR_NewTCPSocket failed\n");
541 failed_already=1;
542 goto def_exit;
544 optData.option = PR_SockOpt_Nonblocking;
545 optData.value.non_blocking = PR_TRUE;
546 PR_SetSocketOption(conn_fd, &optData);
547 rv = PR_Connect(conn_fd, &netaddr, PR_INTERVAL_NO_TIMEOUT);
548 if (rv == PR_FAILURE) {
549 DPRINTF(("PR_Connect to a non-listen port failed: (%d, %d)\n",
550 PR_GetError(), PR_GetOSError()));
551 } else {
552 PR_ASSERT(rv == PR_SUCCESS);
553 fprintf(stderr,"Error - PR_Connect succeeded, expected to fail\n");
554 failed_already=1;
555 goto def_exit;
557 pd.fd = conn_fd;
558 pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
559 #ifndef XP_MAC
560 n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
561 #else
562 n = PR_Poll(&pd, 1, timeout);
563 #endif
564 if (n == -1) {
565 fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
566 PR_GetError(), PR_GetOSError());
567 failed_already=1;
568 goto def_exit;
570 if (PR_GetConnectStatus(&pd) == PR_SUCCESS) {
571 PRInt32 rv;
572 fprintf(stderr,"PR_GetConnectStatus succeeded, expected to fail\n");
573 failed_already = 1;
574 goto def_exit;
576 rv = PR_GetError();
577 DPRINTF(("Connection failed, successfully with PR_Error %d\n",rv));
578 def_exit:
579 if (sockfd) {
580 PR_Close(sockfd);
581 sockfd = NULL;
583 if (conn_fd) {
584 PR_Close(conn_fd);
585 conn_fd = NULL;
587 if (failed_already)
588 return 1;
589 else
590 return 0;