backout 29799f914cab, Bug 917642 - [Helix] Please update the helix blobs
[gecko.git] / nsprpub / pr / tests / nbconn.c
blobe113a7e180ed8926fbae6dd4a9d3b0c243ef23ae
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/. */
6 /*
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.
27 #include "nspr.h"
28 #include "plgetopt.h"
29 #include <stdio.h>
30 #include <string.h>
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 */
39 } Server_Param;
40 static void PR_CALLBACK TCP_Server(void *arg);
42 int _debug_on;
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)
50 PRHostEnt he;
51 char buf[1024];
52 PRNetAddr addr;
53 PRPollDesc pd;
54 PRStatus rv;
55 PRSocketOptionData optData;
56 const char *hostname = NULL;
57 PRIntn default_case, n, bytes_read, bytes_sent;
58 PRInt32 failed_already = 0;
61 * -d debug mode
64 PLOptStatus os;
65 PLOptState *opt = PL_CreateOptState(argc, argv, "d");
66 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
68 if (PL_OPT_BAD == os) continue;
69 switch (opt->option)
71 case 0: /* debug mode */
72 hostname = opt->value;
73 break;
74 case 'd': /* debug mode */
75 _debug_on = 1;
76 break;
77 default:
78 break;
81 PL_DestroyOptState(opt);
83 PR_STDIO_INIT();
84 if (hostname)
85 default_case = 0;
86 else
87 default_case = 1;
89 if (default_case) {
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();
99 if (rv == 0)
100 rv = connection_failure_test();
101 return rv;
102 } else {
103 PRFileDesc *sock;
105 if (PR_GetHostByName(argv[1], buf, sizeof(buf), &he) == PR_FAILURE) {
106 printf( "Unknown host: %s\n", argv[1]);
107 exit(1);
108 } else {
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");
122 pd.fd = sock;
123 pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
124 n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
125 if (n == -1) {
126 printf( "PR_Poll failed\n");
127 exit(1);
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;
151 while (1) {
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);
156 if (n <= 0) {
157 break;
159 PR_Write(PR_STDOUT, buf, n);
161 } else {
162 if (PR_GetError() == PR_IN_PROGRESS_ERROR) {
163 printf( "PR_GetConnectStatus: connect still in progress\n");
164 exit(1);
166 printf( "PR_GetConnectStatus: connect failed: (%ld, %ld)\n",
167 PR_GetError(), PR_GetOSError());
169 PR_Close(sock);
170 printf( "PASS\n");
171 return 0;
178 * TCP Server
179 * Server Thread
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;
190 sockfd = sp->sp_fd;
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());
195 return;
197 bytes_read = 0;
198 while (bytes_read != DATA_BUF_SIZE) {
199 rv = PR_Read(newsockfd, data_buf + bytes_read ,
200 DATA_BUF_SIZE - bytes_read);
201 if (rv < 0) {
202 fprintf(stderr,"Error - PR_Read failed: (%d, %d)\n",
203 PR_GetError(), PR_GetOSError());
204 PR_Close(newsockfd);
205 return;
207 PR_ASSERT(rv != 0);
208 bytes_read += rv;
210 DPRINTF(("Bytes read from client - %d\n",bytes_read));
211 rv = PR_Write(newsockfd, data_buf,DATA_BUF_SIZE);
212 if (rv < 0) {
213 fprintf(stderr,"Error - PR_Write failed: (%d, %d)\n",
214 PR_GetError(), PR_GetOSError());
215 PR_Close(newsockfd);
216 return;
218 PR_ASSERT(rv == DATA_BUF_SIZE);
219 DPRINTF(("Bytes written to client - %d\n",rv));
220 PR_Close(newsockfd);
225 * test for successful connection using a non-blocking socket
227 static PRIntn
228 connection_success_test()
230 PRFileDesc *sockfd = NULL, *conn_fd = NULL;
231 PRNetAddr netaddr;
232 PRInt32 i, rv;
233 PRPollDesc pd;
234 PRSocketOptionData optData;
235 PRThread *thr = NULL;
236 Server_Param sp;
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");
246 failed_already=1;
247 goto def_exit;
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
255 * use
257 i = 0;
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)
262 continue;
264 fprintf(stderr,"ERROR - PR_Bind failed: (%d,%d)\n",
265 PR_GetError(), PR_GetOSError());
266 failed_already=1;
267 goto def_exit;
270 if (PR_Listen(sockfd, 32) < 0) {
271 fprintf(stderr,"ERROR - PR_Listen failed: (%d,%d)\n",
272 PR_GetError(), PR_GetOSError());
273 failed_already=1;
274 goto def_exit;
277 if (PR_GetSockName(sockfd, &netaddr) < 0) {
278 fprintf(stderr,"ERROR - PR_GetSockName failed: (%d,%d)\n",
279 PR_GetError(), PR_GetOSError());
280 failed_already=1;
281 goto def_exit;
283 if ((conn_fd = PR_NewTCPSocket()) == NULL) {
284 fprintf(stderr,"Error - PR_NewTCPSocket failed\n");
285 failed_already=1;
286 goto def_exit;
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"));
295 } else {
296 fprintf(stderr,"Error - PR_Connect failed: (%d, %d)\n",
297 PR_GetError(), PR_GetOSError());
298 failed_already=1;
299 goto def_exit;
303 * Now create a thread to accept a connection
305 sp.sp_fd = sockfd;
306 thr = PR_CreateThread(PR_USER_THREAD, TCP_Server, (void *)&sp,
307 PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
308 if (thr == NULL) {
309 fprintf(stderr,"Error - PR_CreateThread failed: (%d,%d)\n",
310 PR_GetError(), PR_GetOSError());
311 failed_already=1;
312 goto def_exit;
314 DPRINTF(("Created TCP_Server thread [0x%x]\n",thr));
315 pd.fd = conn_fd;
316 pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
317 n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
318 if (n == -1) {
319 fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
320 PR_GetError(), PR_GetOSError());
321 failed_already=1;
322 goto def_exit;
324 if (PR_GetConnectStatus(&pd) == PR_SUCCESS) {
325 PRInt32 rv;
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;
334 bytes_sent = 0;
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);
338 if (rv < 0) {
339 fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
340 PR_GetError(), PR_GetOSError());
341 failed_already=1;
342 goto def_exit;
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);
347 if (rv < 0) {
348 fprintf(stderr,"Error - PR_Write failed: (%d, %d)\n",
349 PR_GetError(), PR_GetOSError());
350 failed_already=1;
351 goto def_exit;
353 PR_ASSERT(rv > 0);
354 bytes_sent += rv;
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;
359 bytes_read = 0;
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);
363 if (rv < 0) {
364 fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
365 PR_GetError(), PR_GetOSError());
366 failed_already=1;
367 goto def_exit;
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);
372 if (rv < 0) {
373 fprintf(stderr,"Error - PR_Read failed: (%d, %d)\n",
374 PR_GetError(), PR_GetOSError());
375 failed_already=1;
376 goto def_exit;
378 PR_ASSERT(rv != 0);
379 bytes_read += rv;
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");
387 failed_already=1;
388 goto def_exit;
390 DPRINTF(("Data integrity verified\n"));
391 } else {
392 fprintf(stderr,"PR_GetConnectStatus: connect failed: (%ld, %ld)\n",
393 PR_GetError(), PR_GetOSError());
394 failed_already = 1;
395 goto def_exit;
397 def_exit:
398 if (thr) {
399 PR_JoinThread(thr);
400 thr = NULL;
402 if (sockfd) {
403 PR_Close(sockfd);
404 sockfd = NULL;
406 if (conn_fd) {
407 PR_Close(conn_fd);
408 conn_fd = NULL;
410 if (failed_already)
411 return 1;
412 else
413 return 0;
418 * test for connection to a nonexistent port using a non-blocking socket
420 static PRIntn
421 connection_failure_test()
423 PRFileDesc *sockfd = NULL, *conn_fd = NULL;
424 PRNetAddr netaddr;
425 PRInt32 i, rv;
426 PRPollDesc pd;
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");
435 failed_already=1;
436 goto def_exit;
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
444 * use
446 i = 0;
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)
451 continue;
453 fprintf(stderr,"ERROR - PR_Bind failed: (%d,%d)\n",
454 PR_GetError(), PR_GetOSError());
455 failed_already=1;
456 goto def_exit;
459 if (PR_GetSockName(sockfd, &netaddr) < 0) {
460 fprintf(stderr,"ERROR - PR_GetSockName failed: (%d,%d)\n",
461 PR_GetError(), PR_GetOSError());
462 failed_already=1;
463 goto def_exit;
465 #ifdef AIX
467 * On AIX, set to unused/reserved port
469 netaddr.inet.port = PR_htons(TCP_UNUSED_PORT);
470 #endif
471 if ((conn_fd = PR_NewTCPSocket()) == NULL) {
472 fprintf(stderr,"Error - PR_NewTCPSocket failed\n");
473 failed_already=1;
474 goto def_exit;
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()));
483 } else {
484 PR_ASSERT(rv == PR_SUCCESS);
485 fprintf(stderr,"Error - PR_Connect succeeded, expected to fail\n");
486 failed_already=1;
487 goto def_exit;
489 pd.fd = conn_fd;
490 pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
491 n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
492 if (n == -1) {
493 fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
494 PR_GetError(), PR_GetOSError());
495 failed_already=1;
496 goto def_exit;
498 if (PR_GetConnectStatus(&pd) == PR_SUCCESS) {
499 PRInt32 rv;
500 fprintf(stderr,"PR_GetConnectStatus succeeded, expected to fail\n");
501 failed_already = 1;
502 goto def_exit;
504 rv = PR_GetError();
505 DPRINTF(("Connection failed, successfully with PR_Error %d\n",rv));
506 def_exit:
507 if (sockfd) {
508 PR_Close(sockfd);
509 sockfd = NULL;
511 if (conn_fd) {
512 PR_Close(conn_fd);
513 conn_fd = NULL;
515 if (failed_already)
516 return 1;
517 else
518 return 0;