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/. */
7 * A test for nonblocking connect. Functions tested include PR_Connect,
8 * PR_Poll, and PR_GetConnectStatus.
10 * The test should be invoked with a host name, for example:
11 * nbconn www.netscape.com
12 * It will do a nonblocking connect to port 80 (HTTP) on that host,
13 * and when connected, issue the "GET /" HTTP command.
15 * You should run this test in three ways:
16 * 1. To a known web site, such as www.netscape.com. The HTML of the
17 * top-level page at the web site should be printed.
18 * 2. To a machine not running a web server at port 80. This test should
19 * fail. Ideally the error code should be PR_CONNECT_REFUSED_ERROR.
20 * But it is possible to return PR_UNKNOWN_ERROR on certain platforms.
21 * 3. To an unreachable machine, for example, a machine that is off line.
22 * The test should fail after the connect times out. Ideally the
23 * error code should be PR_IO_TIMEOUT_ERROR, but it is possible to
24 * return PR_UNKNOWN_ERROR on certain platforms.
32 #define SERVER_MAX_BIND_COUNT 100
33 #define DATA_BUF_SIZE 256
34 #define TCP_SERVER_PORT 10000
35 #define TCP_UNUSED_PORT 211
37 typedef struct Server_Param
{
38 PRFileDesc
*sp_fd
; /* server port */
40 static void PR_CALLBACK
TCP_Server(void *arg
);
43 #define DPRINTF(arg) if (_debug_on) printf arg
45 static PRIntn
connection_success_test();
46 static PRIntn
connection_failure_test();
48 int main(int argc
, char **argv
)
55 PRSocketOptionData optData
;
56 const char *hostname
= NULL
;
57 PRIntn default_case
, n
, bytes_read
, bytes_sent
;
58 PRInt32 failed_already
= 0;
65 PLOptState
*opt
= PL_CreateOptState(argc
, argv
, "d");
66 while (PL_OPT_EOL
!= (os
= PL_GetNextOpt(opt
)))
68 if (PL_OPT_BAD
== os
) continue;
71 case 0: /* debug mode */
72 hostname
= opt
->value
;
74 case 'd': /* debug mode */
81 PL_DestroyOptState(opt
);
92 * In the default case the following tests are executed:
93 * 1. successful connection: a server thread accepts a connection
94 * from the main thread
95 * 2. unsuccessful connection: the main thread tries to connect to a
96 * nonexistent port and expects to get an error
98 rv
= connection_success_test();
100 rv
= connection_failure_test();
105 if (PR_GetHostByName(argv
[1], buf
, sizeof(buf
), &he
) == PR_FAILURE
) {
106 printf( "Unknown host: %s\n", argv
[1]);
109 printf( "host: %s\n", buf
);
111 PR_EnumerateHostEnt(0, &he
, 80, &addr
);
113 sock
= PR_NewTCPSocket();
114 optData
.option
= PR_SockOpt_Nonblocking
;
115 optData
.value
.non_blocking
= PR_TRUE
;
116 PR_SetSocketOption(sock
, &optData
);
117 rv
= PR_Connect(sock
, &addr
, PR_INTERVAL_NO_TIMEOUT
);
118 if (rv
== PR_FAILURE
&& PR_GetError() == PR_IN_PROGRESS_ERROR
) {
119 printf( "Connect in progress\n");
123 pd
.in_flags
= PR_POLL_WRITE
| PR_POLL_EXCEPT
;
124 n
= PR_Poll(&pd
, 1, PR_INTERVAL_NO_TIMEOUT
);
126 printf( "PR_Poll failed\n");
129 printf( "PR_Poll returns %d\n", n
);
130 if (pd
.out_flags
& PR_POLL_READ
) {
131 printf( "PR_POLL_READ\n");
133 if (pd
.out_flags
& PR_POLL_WRITE
) {
134 printf( "PR_POLL_WRITE\n");
136 if (pd
.out_flags
& PR_POLL_EXCEPT
) {
137 printf( "PR_POLL_EXCEPT\n");
139 if (pd
.out_flags
& PR_POLL_ERR
) {
140 printf( "PR_POLL_ERR\n");
142 if (pd
.out_flags
& PR_POLL_NVAL
) {
143 printf( "PR_POLL_NVAL\n");
146 if (PR_GetConnectStatus(&pd
) == PR_SUCCESS
) {
147 printf("PR_GetConnectStatus: connect succeeded\n");
148 PR_Write(sock
, "GET /\r\n\r\n", 9);
149 PR_Shutdown(sock
, PR_SHUTDOWN_SEND
);
150 pd
.in_flags
= PR_POLL_READ
;
152 n
= PR_Poll(&pd
, 1, PR_INTERVAL_NO_TIMEOUT
);
153 printf( "poll returns %d\n", n
);
154 n
= PR_Read(sock
, buf
, sizeof(buf
));
155 printf( "read returns %d\n", n
);
159 PR_Write(PR_STDOUT
, buf
, n
);
162 if (PR_GetError() == PR_IN_PROGRESS_ERROR
) {
163 printf( "PR_GetConnectStatus: connect still in progress\n");
166 printf( "PR_GetConnectStatus: connect failed: (%ld, %ld)\n",
167 PR_GetError(), PR_GetOSError());
180 * Accept a connection from the client and write some data
182 static void PR_CALLBACK
183 TCP_Server(void *arg
)
185 Server_Param
*sp
= (Server_Param
*) arg
;
186 PRFileDesc
*sockfd
, *newsockfd
;
187 char data_buf
[DATA_BUF_SIZE
];
188 PRIntn rv
, bytes_read
;
191 if ((newsockfd
= PR_Accept(sockfd
, NULL
,
192 PR_INTERVAL_NO_TIMEOUT
)) == NULL
) {
193 fprintf(stderr
,"ERROR - PR_Accept failed: (%d,%d)\n",
194 PR_GetError(), PR_GetOSError());
198 while (bytes_read
!= DATA_BUF_SIZE
) {
199 rv
= PR_Read(newsockfd
, data_buf
+ bytes_read
,
200 DATA_BUF_SIZE
- bytes_read
);
202 fprintf(stderr
,"Error - PR_Read failed: (%d, %d)\n",
203 PR_GetError(), PR_GetOSError());
210 DPRINTF(("Bytes read from client - %d\n",bytes_read
));
211 rv
= PR_Write(newsockfd
, data_buf
,DATA_BUF_SIZE
);
213 fprintf(stderr
,"Error - PR_Write failed: (%d, %d)\n",
214 PR_GetError(), PR_GetOSError());
218 PR_ASSERT(rv
== DATA_BUF_SIZE
);
219 DPRINTF(("Bytes written to client - %d\n",rv
));
225 * test for successful connection using a non-blocking socket
228 connection_success_test()
230 PRFileDesc
*sockfd
= NULL
, *conn_fd
= NULL
;
234 PRSocketOptionData optData
;
235 PRThread
*thr
= NULL
;
237 char send_buf
[DATA_BUF_SIZE
], recv_buf
[DATA_BUF_SIZE
];
238 PRIntn default_case
, n
, bytes_read
, bytes_sent
;
239 PRIntn failed_already
= 0;
242 * Create a tcp socket
244 if ((sockfd
= PR_NewTCPSocket()) == NULL
) {
245 fprintf(stderr
,"Error - PR_NewTCPSocket failed\n");
249 memset(&netaddr
, 0 , sizeof(netaddr
));
250 netaddr
.inet
.family
= PR_AF_INET
;
251 netaddr
.inet
.port
= PR_htons(TCP_SERVER_PORT
);
252 netaddr
.inet
.ip
= PR_htonl(PR_INADDR_ANY
);
254 * try a few times to bind server's address, if addresses are in
258 while (PR_Bind(sockfd
, &netaddr
) < 0) {
259 if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR
) {
260 netaddr
.inet
.port
+= 2;
261 if (i
++ < SERVER_MAX_BIND_COUNT
)
264 fprintf(stderr
,"ERROR - PR_Bind failed: (%d,%d)\n",
265 PR_GetError(), PR_GetOSError());
270 if (PR_Listen(sockfd
, 32) < 0) {
271 fprintf(stderr
,"ERROR - PR_Listen failed: (%d,%d)\n",
272 PR_GetError(), PR_GetOSError());
277 if (PR_GetSockName(sockfd
, &netaddr
) < 0) {
278 fprintf(stderr
,"ERROR - PR_GetSockName failed: (%d,%d)\n",
279 PR_GetError(), PR_GetOSError());
283 if ((conn_fd
= PR_NewTCPSocket()) == NULL
) {
284 fprintf(stderr
,"Error - PR_NewTCPSocket failed\n");
288 optData
.option
= PR_SockOpt_Nonblocking
;
289 optData
.value
.non_blocking
= PR_TRUE
;
290 PR_SetSocketOption(conn_fd
, &optData
);
291 rv
= PR_Connect(conn_fd
, &netaddr
, PR_INTERVAL_NO_TIMEOUT
);
292 if (rv
== PR_FAILURE
) {
293 if (PR_GetError() == PR_IN_PROGRESS_ERROR
) {
294 DPRINTF(("Connect in progress\n"));
296 fprintf(stderr
,"Error - PR_Connect failed: (%d, %d)\n",
297 PR_GetError(), PR_GetOSError());
303 * Now create a thread to accept a connection
306 thr
= PR_CreateThread(PR_USER_THREAD
, TCP_Server
, (void *)&sp
,
307 PR_PRIORITY_NORMAL
, PR_LOCAL_THREAD
, PR_JOINABLE_THREAD
, 0);
309 fprintf(stderr
,"Error - PR_CreateThread failed: (%d,%d)\n",
310 PR_GetError(), PR_GetOSError());
314 DPRINTF(("Created TCP_Server thread [0x%x]\n",thr
));
316 pd
.in_flags
= PR_POLL_WRITE
| PR_POLL_EXCEPT
;
317 n
= PR_Poll(&pd
, 1, PR_INTERVAL_NO_TIMEOUT
);
319 fprintf(stderr
,"Error - PR_Poll failed: (%d, %d)\n",
320 PR_GetError(), PR_GetOSError());
324 if (PR_GetConnectStatus(&pd
) == PR_SUCCESS
) {
327 DPRINTF(("Connection successful\n"));
330 * Write some data, read it back and check data integrity to
331 * make sure the connection is good
333 pd
.in_flags
= PR_POLL_WRITE
;
335 memset(send_buf
, 'a', DATA_BUF_SIZE
);
336 while (bytes_sent
!= DATA_BUF_SIZE
) {
337 rv
= PR_Poll(&pd
, 1, PR_INTERVAL_NO_TIMEOUT
);
339 fprintf(stderr
,"Error - PR_Poll failed: (%d, %d)\n",
340 PR_GetError(), PR_GetOSError());
344 PR_ASSERT((rv
== 1) && (pd
.out_flags
== PR_POLL_WRITE
));
345 rv
= PR_Write(conn_fd
, send_buf
+ bytes_sent
,
346 DATA_BUF_SIZE
- bytes_sent
);
348 fprintf(stderr
,"Error - PR_Write failed: (%d, %d)\n",
349 PR_GetError(), PR_GetOSError());
356 DPRINTF(("Bytes written to server - %d\n",bytes_sent
));
357 PR_Shutdown(conn_fd
, PR_SHUTDOWN_SEND
);
358 pd
.in_flags
= PR_POLL_READ
;
360 memset(recv_buf
, 0, DATA_BUF_SIZE
);
361 while (bytes_read
!= DATA_BUF_SIZE
) {
362 rv
= PR_Poll(&pd
, 1, PR_INTERVAL_NO_TIMEOUT
);
364 fprintf(stderr
,"Error - PR_Poll failed: (%d, %d)\n",
365 PR_GetError(), PR_GetOSError());
369 PR_ASSERT((rv
== 1) && (pd
.out_flags
== PR_POLL_READ
));
370 rv
= PR_Read(conn_fd
, recv_buf
+ bytes_read
,
371 DATA_BUF_SIZE
- bytes_read
);
373 fprintf(stderr
,"Error - PR_Read failed: (%d, %d)\n",
374 PR_GetError(), PR_GetOSError());
381 DPRINTF(("Bytes read from server - %d\n",bytes_read
));
383 * verify the data read
385 if (memcmp(send_buf
, recv_buf
, DATA_BUF_SIZE
) != 0) {
386 fprintf(stderr
,"ERROR - data corruption\n");
390 DPRINTF(("Data integrity verified\n"));
392 fprintf(stderr
,"PR_GetConnectStatus: connect failed: (%ld, %ld)\n",
393 PR_GetError(), PR_GetOSError());
418 * test for connection to a nonexistent port using a non-blocking socket
421 connection_failure_test()
423 PRFileDesc
*sockfd
= NULL
, *conn_fd
= NULL
;
427 PRSocketOptionData optData
;
428 PRIntn n
, failed_already
= 0;
431 * Create a tcp socket
433 if ((sockfd
= PR_NewTCPSocket()) == NULL
) {
434 fprintf(stderr
,"Error - PR_NewTCPSocket failed\n");
438 memset(&netaddr
, 0 , sizeof(netaddr
));
439 netaddr
.inet
.family
= PR_AF_INET
;
440 netaddr
.inet
.port
= PR_htons(TCP_SERVER_PORT
);
441 netaddr
.inet
.ip
= PR_htonl(PR_INADDR_ANY
);
443 * try a few times to bind server's address, if addresses are in
447 while (PR_Bind(sockfd
, &netaddr
) < 0) {
448 if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR
) {
449 netaddr
.inet
.port
+= 2;
450 if (i
++ < SERVER_MAX_BIND_COUNT
)
453 fprintf(stderr
,"ERROR - PR_Bind failed: (%d,%d)\n",
454 PR_GetError(), PR_GetOSError());
459 if (PR_GetSockName(sockfd
, &netaddr
) < 0) {
460 fprintf(stderr
,"ERROR - PR_GetSockName failed: (%d,%d)\n",
461 PR_GetError(), PR_GetOSError());
467 * On AIX, set to unused/reserved port
469 netaddr
.inet
.port
= PR_htons(TCP_UNUSED_PORT
);
471 if ((conn_fd
= PR_NewTCPSocket()) == NULL
) {
472 fprintf(stderr
,"Error - PR_NewTCPSocket failed\n");
476 optData
.option
= PR_SockOpt_Nonblocking
;
477 optData
.value
.non_blocking
= PR_TRUE
;
478 PR_SetSocketOption(conn_fd
, &optData
);
479 rv
= PR_Connect(conn_fd
, &netaddr
, PR_INTERVAL_NO_TIMEOUT
);
480 if (rv
== PR_FAILURE
) {
481 DPRINTF(("PR_Connect to a non-listen port failed: (%d, %d)\n",
482 PR_GetError(), PR_GetOSError()));
484 PR_ASSERT(rv
== PR_SUCCESS
);
485 fprintf(stderr
,"Error - PR_Connect succeeded, expected to fail\n");
490 pd
.in_flags
= PR_POLL_WRITE
| PR_POLL_EXCEPT
;
491 n
= PR_Poll(&pd
, 1, PR_INTERVAL_NO_TIMEOUT
);
493 fprintf(stderr
,"Error - PR_Poll failed: (%d, %d)\n",
494 PR_GetError(), PR_GetOSError());
498 if (PR_GetConnectStatus(&pd
) == PR_SUCCESS
) {
500 fprintf(stderr
,"PR_GetConnectStatus succeeded, expected to fail\n");
505 DPRINTF(("Connection failed, successfully with PR_Error %d\n",rv
));