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
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.
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.
65 #define printf PR_LogPrint
66 extern void SetupMacPrintfLog(char *logFile
);
67 static char *hosts
[4] = {"cynic", "warp", "gandalf", "neon"};
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 */
78 static void PR_CALLBACK
TCP_Server(void *arg
);
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
)
93 PRSocketOptionData optData
;
94 const char *hostname
= NULL
;
95 PRIntn default_case
, n
, bytes_read
, bytes_sent
;
96 PRInt32 failed_already
= 0;
99 PRIntervalTime timeout
;
107 PLOptState
*opt
= PL_CreateOptState(argc
, argv
, "d");
108 while (PL_OPT_EOL
!= (os
= PL_GetNextOpt(opt
)))
110 if (PL_OPT_BAD
== os
) continue;
113 case 0: /* debug mode */
114 hostname
= opt
->value
;
116 case 'd': /* debug mode */
123 PL_DestroyOptState(opt
);
126 SetupMacPrintfLog("nbconn.log");
127 for (index
=0; index
<4; index
++) {
128 argv
[1] = hosts
[index
];
129 timeout
= PR_INTERVAL_NO_TIMEOUT
;
131 timeout
= PR_SecondsToInterval(10UL);
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();
154 rv
= connection_failure_test();
159 if (PR_GetHostByName(argv
[1], buf
, sizeof(buf
), &he
) == PR_FAILURE
) {
160 printf( "Unknown host: %s\n", argv
[1]);
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");
177 pd
.in_flags
= PR_POLL_WRITE
| PR_POLL_EXCEPT
;
179 n
= PR_Poll(&pd
, 1, PR_INTERVAL_NO_TIMEOUT
);
181 n
= PR_Poll(&pd
, 1, timeout
);
184 printf( "PR_Poll failed\n");
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
;
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
);
219 PR_Write(PR_STDOUT
, buf
, n
);
223 if (PR_GetError() == PR_IN_PROGRESS_ERROR
) {
224 printf( "PR_GetConnectStatus: connect still in progress\n");
227 printf( "PR_GetConnectStatus: connect failed: (%ld, %ld)\n",
228 PR_GetError(), PR_GetOSError());
232 } /* end of for loop */
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
;
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());
262 while (bytes_read
!= DATA_BUF_SIZE
) {
263 rv
= PR_Read(newsockfd
, data_buf
+ bytes_read
,
264 DATA_BUF_SIZE
- bytes_read
);
266 fprintf(stderr
,"Error - PR_Read failed: (%d, %d)\n",
267 PR_GetError(), PR_GetOSError());
274 DPRINTF(("Bytes read from client - %d\n",bytes_read
));
275 rv
= PR_Write(newsockfd
, data_buf
,DATA_BUF_SIZE
);
277 fprintf(stderr
,"Error - PR_Write failed: (%d, %d)\n",
278 PR_GetError(), PR_GetOSError());
282 PR_ASSERT(rv
== DATA_BUF_SIZE
);
283 DPRINTF(("Bytes written to client - %d\n",rv
));
289 * test for successful connection using a non-blocking socket
292 connection_success_test()
294 PRFileDesc
*sockfd
= NULL
, *conn_fd
= NULL
;
298 PRSocketOptionData optData
;
299 PRThread
*thr
= NULL
;
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");
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
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
)
328 fprintf(stderr
,"ERROR - PR_Bind failed: (%d,%d)\n",
329 PR_GetError(), PR_GetOSError());
334 if (PR_Listen(sockfd
, 32) < 0) {
335 fprintf(stderr
,"ERROR - PR_Listen failed: (%d,%d)\n",
336 PR_GetError(), PR_GetOSError());
341 if (PR_GetSockName(sockfd
, &netaddr
) < 0) {
342 fprintf(stderr
,"ERROR - PR_GetSockName failed: (%d,%d)\n",
343 PR_GetError(), PR_GetOSError());
347 if ((conn_fd
= PR_NewTCPSocket()) == NULL
) {
348 fprintf(stderr
,"Error - PR_NewTCPSocket failed\n");
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"));
360 fprintf(stderr
,"Error - PR_Connect failed: (%d, %d)\n",
361 PR_GetError(), PR_GetOSError());
367 * Now create a thread to accept a connection
370 thr
= PR_CreateThread(PR_USER_THREAD
, TCP_Server
, (void *)&sp
,
371 PR_PRIORITY_NORMAL
, PR_LOCAL_THREAD
, PR_JOINABLE_THREAD
, 0);
373 fprintf(stderr
,"Error - PR_CreateThread failed: (%d,%d)\n",
374 PR_GetError(), PR_GetOSError());
378 DPRINTF(("Created TCP_Server thread [0x%x]\n",thr
));
380 pd
.in_flags
= PR_POLL_WRITE
| PR_POLL_EXCEPT
;
382 n
= PR_Poll(&pd
, 1, PR_INTERVAL_NO_TIMEOUT
);
384 n
= PR_Poll(&pd
, 1, timeout
);
387 fprintf(stderr
,"Error - PR_Poll failed: (%d, %d)\n",
388 PR_GetError(), PR_GetOSError());
392 if (PR_GetConnectStatus(&pd
) == PR_SUCCESS
) {
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
;
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
);
407 fprintf(stderr
,"Error - PR_Poll failed: (%d, %d)\n",
408 PR_GetError(), PR_GetOSError());
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
);
416 fprintf(stderr
,"Error - PR_Write failed: (%d, %d)\n",
417 PR_GetError(), PR_GetOSError());
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
;
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
);
432 fprintf(stderr
,"Error - PR_Poll failed: (%d, %d)\n",
433 PR_GetError(), PR_GetOSError());
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
);
441 fprintf(stderr
,"Error - PR_Read failed: (%d, %d)\n",
442 PR_GetError(), PR_GetOSError());
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");
458 DPRINTF(("Data integrity verified\n"));
460 fprintf(stderr
,"PR_GetConnectStatus: connect failed: (%ld, %ld)\n",
461 PR_GetError(), PR_GetOSError());
486 * test for connection to a non-existent port using a non-blocking socket
489 connection_failure_test()
491 PRFileDesc
*sockfd
= NULL
, *conn_fd
= NULL
;
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");
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
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
)
521 fprintf(stderr
,"ERROR - PR_Bind failed: (%d,%d)\n",
522 PR_GetError(), PR_GetOSError());
527 if (PR_GetSockName(sockfd
, &netaddr
) < 0) {
528 fprintf(stderr
,"ERROR - PR_GetSockName failed: (%d,%d)\n",
529 PR_GetError(), PR_GetOSError());
535 * On AIX, set to unused/reserved port
537 netaddr
.inet
.port
= PR_htons(TCP_UNUSED_PORT
);
539 if ((conn_fd
= PR_NewTCPSocket()) == NULL
) {
540 fprintf(stderr
,"Error - PR_NewTCPSocket failed\n");
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()));
552 PR_ASSERT(rv
== PR_SUCCESS
);
553 fprintf(stderr
,"Error - PR_Connect succeeded, expected to fail\n");
558 pd
.in_flags
= PR_POLL_WRITE
| PR_POLL_EXCEPT
;
560 n
= PR_Poll(&pd
, 1, PR_INTERVAL_NO_TIMEOUT
);
562 n
= PR_Poll(&pd
, 1, timeout
);
565 fprintf(stderr
,"Error - PR_Poll failed: (%d, %d)\n",
566 PR_GetError(), PR_GetOSError());
570 if (PR_GetConnectStatus(&pd
) == PR_SUCCESS
) {
572 fprintf(stderr
,"PR_GetConnectStatus succeeded, expected to fail\n");
577 DPRINTF(("Connection failed, successfully with PR_Error %d\n",rv
));