|
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 #ifdef WIN32 |
|
7 #include <windows.h> |
|
8 #endif |
|
9 |
|
10 #ifdef XP_UNIX |
|
11 #include <unistd.h> /* for close() */ |
|
12 #endif |
|
13 |
|
14 #include "prinit.h" |
|
15 #include "prio.h" |
|
16 #include "prlog.h" |
|
17 #include "prprf.h" |
|
18 #include "prnetdb.h" |
|
19 |
|
20 #include "private/pprio.h" |
|
21 |
|
22 #define CLIENT_LOOPS 5 |
|
23 #define BUF_SIZE 128 |
|
24 |
|
25 #include <stdio.h> |
|
26 #include <string.h> |
|
27 #include <stdlib.h> |
|
28 |
|
29 #ifdef WINCE |
|
30 |
|
31 int main(int argc, char **argv) |
|
32 { |
|
33 fprintf(stderr, "Invalid/Broken Test for WinCE/WinMobile\n"); |
|
34 exit(1); |
|
35 } |
|
36 |
|
37 #else |
|
38 |
|
39 static void |
|
40 clientThreadFunc(void *arg) |
|
41 { |
|
42 PRUint16 port = (PRUint16) arg; |
|
43 PRFileDesc *sock; |
|
44 PRNetAddr addr; |
|
45 char buf[BUF_SIZE]; |
|
46 int i; |
|
47 |
|
48 addr.inet.family = PR_AF_INET; |
|
49 addr.inet.port = PR_htons(port); |
|
50 addr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK); |
|
51 PR_snprintf(buf, sizeof(buf), "%hu", port); |
|
52 |
|
53 for (i = 0; i < 5; i++) { |
|
54 sock = PR_NewTCPSocket(); |
|
55 PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT); |
|
56 |
|
57 PR_Write(sock, buf, sizeof(buf)); |
|
58 PR_Close(sock); |
|
59 } |
|
60 } |
|
61 |
|
62 int main(int argc, char **argv) |
|
63 { |
|
64 PRFileDesc *listenSock1, *listenSock2; |
|
65 PRFileDesc *badFD; |
|
66 PRUint16 listenPort1, listenPort2; |
|
67 PRNetAddr addr; |
|
68 char buf[BUF_SIZE]; |
|
69 PRThread *clientThread; |
|
70 PRPollDesc pds0[10], pds1[10], *pds, *other_pds; |
|
71 PRIntn npds; |
|
72 PRInt32 retVal; |
|
73 PRInt32 rv; |
|
74 PROsfd sd; |
|
75 struct sockaddr_in saddr; |
|
76 PRIntn saddr_len; |
|
77 PRUint16 listenPort3; |
|
78 PRFileDesc *socket_poll_fd; |
|
79 PRIntn i, j; |
|
80 |
|
81 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); |
|
82 PR_STDIO_INIT(); |
|
83 |
|
84 printf("This program tests PR_Poll with sockets.\n"); |
|
85 printf("Timeout, error reporting, and normal operation are tested.\n\n"); |
|
86 |
|
87 /* Create two listening sockets */ |
|
88 if ((listenSock1 = PR_NewTCPSocket()) == NULL) { |
|
89 fprintf(stderr, "Can't create a new TCP socket\n"); |
|
90 exit(1); |
|
91 } |
|
92 addr.inet.family = PR_AF_INET; |
|
93 addr.inet.ip = PR_htonl(PR_INADDR_ANY); |
|
94 addr.inet.port = PR_htons(0); |
|
95 if (PR_Bind(listenSock1, &addr) == PR_FAILURE) { |
|
96 fprintf(stderr, "Can't bind socket\n"); |
|
97 exit(1); |
|
98 } |
|
99 if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) { |
|
100 fprintf(stderr, "PR_GetSockName failed\n"); |
|
101 exit(1); |
|
102 } |
|
103 listenPort1 = PR_ntohs(addr.inet.port); |
|
104 if (PR_Listen(listenSock1, 5) == PR_FAILURE) { |
|
105 fprintf(stderr, "Can't listen on a socket\n"); |
|
106 exit(1); |
|
107 } |
|
108 |
|
109 if ((listenSock2 = PR_NewTCPSocket()) == NULL) { |
|
110 fprintf(stderr, "Can't create a new TCP socket\n"); |
|
111 exit(1); |
|
112 } |
|
113 addr.inet.family = PR_AF_INET; |
|
114 addr.inet.ip = PR_htonl(PR_INADDR_ANY); |
|
115 addr.inet.port = PR_htons(0); |
|
116 if (PR_Bind(listenSock2, &addr) == PR_FAILURE) { |
|
117 fprintf(stderr, "Can't bind socket\n"); |
|
118 exit(1); |
|
119 } |
|
120 if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) { |
|
121 fprintf(stderr, "PR_GetSockName failed\n"); |
|
122 exit(1); |
|
123 } |
|
124 listenPort2 = PR_ntohs(addr.inet.port); |
|
125 if (PR_Listen(listenSock2, 5) == PR_FAILURE) { |
|
126 fprintf(stderr, "Can't listen on a socket\n"); |
|
127 exit(1); |
|
128 } |
|
129 /* Set up the poll descriptor array */ |
|
130 pds = pds0; |
|
131 other_pds = pds1; |
|
132 memset(pds, 0, sizeof(pds)); |
|
133 npds = 0; |
|
134 pds[npds].fd = listenSock1; |
|
135 pds[npds].in_flags = PR_POLL_READ; |
|
136 npds++; |
|
137 pds[npds].fd = listenSock2; |
|
138 pds[npds].in_flags = PR_POLL_READ; |
|
139 npds++; |
|
140 |
|
141 sd = socket(AF_INET, SOCK_STREAM, 0); |
|
142 PR_ASSERT(sd >= 0); |
|
143 memset((char *) &saddr, 0, sizeof(saddr)); |
|
144 saddr.sin_family = AF_INET; |
|
145 saddr.sin_addr.s_addr = htonl(INADDR_ANY); |
|
146 saddr.sin_port = htons(0); |
|
147 |
|
148 rv = bind(sd, (struct sockaddr *)&saddr, sizeof(saddr)); |
|
149 PR_ASSERT(rv == 0); |
|
150 saddr_len = sizeof(saddr); |
|
151 rv = getsockname(sd, (struct sockaddr *) &saddr, &saddr_len); |
|
152 PR_ASSERT(rv == 0); |
|
153 listenPort3 = ntohs(saddr.sin_port); |
|
154 |
|
155 rv = listen(sd, 5); |
|
156 PR_ASSERT(rv == 0); |
|
157 pds[npds].fd = socket_poll_fd = PR_CreateSocketPollFd(sd); |
|
158 PR_ASSERT(pds[npds].fd); |
|
159 pds[npds].in_flags = PR_POLL_READ; |
|
160 npds++; |
|
161 PR_snprintf(buf, sizeof(buf), |
|
162 "The server thread is listening on ports %hu, %hu and %hu\n\n", |
|
163 listenPort1, listenPort2, listenPort3); |
|
164 printf("%s", buf); |
|
165 |
|
166 /* Testing timeout */ |
|
167 printf("PR_Poll should time out in 5 seconds\n"); |
|
168 retVal = PR_Poll(pds, npds, PR_SecondsToInterval(5)); |
|
169 if (retVal != 0) { |
|
170 PR_snprintf(buf, sizeof(buf), |
|
171 "PR_Poll should time out and return 0, but it returns %ld\n", |
|
172 retVal); |
|
173 fprintf(stderr, "%s", buf); |
|
174 exit(1); |
|
175 } |
|
176 printf("PR_Poll timed out. Test passed.\n\n"); |
|
177 |
|
178 /* Testing bad fd */ |
|
179 printf("PR_Poll should detect a bad file descriptor\n"); |
|
180 if ((badFD = PR_NewTCPSocket()) == NULL) { |
|
181 fprintf(stderr, "Can't create a TCP socket\n"); |
|
182 exit(1); |
|
183 } |
|
184 |
|
185 pds[npds].fd = badFD; |
|
186 pds[npds].in_flags = PR_POLL_READ; |
|
187 npds++; |
|
188 PR_Close(badFD); /* make the fd bad */ |
|
189 #if 0 |
|
190 retVal = PR_Poll(pds, npds, PR_INTERVAL_NO_TIMEOUT); |
|
191 if (retVal != 1 || (unsigned short) pds[2].out_flags != PR_POLL_NVAL) { |
|
192 fprintf(stderr, "Failed to detect the bad fd: " |
|
193 "PR_Poll returns %d, out_flags is 0x%hx\n", |
|
194 retVal, pds[npds - 1].out_flags); |
|
195 exit(1); |
|
196 } |
|
197 printf("PR_Poll detected the bad fd. Test passed.\n\n"); |
|
198 #endif |
|
199 npds--; |
|
200 |
|
201 clientThread = PR_CreateThread(PR_USER_THREAD, |
|
202 clientThreadFunc, (void *) listenPort1, |
|
203 PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, |
|
204 PR_UNJOINABLE_THREAD, 0); |
|
205 if (clientThread == NULL) { |
|
206 fprintf(stderr, "can't create thread\n"); |
|
207 exit(1); |
|
208 } |
|
209 |
|
210 clientThread = PR_CreateThread(PR_USER_THREAD, |
|
211 clientThreadFunc, (void *) listenPort2, |
|
212 PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, |
|
213 PR_UNJOINABLE_THREAD, 0); |
|
214 if (clientThread == NULL) { |
|
215 fprintf(stderr, "can't create thread\n"); |
|
216 exit(1); |
|
217 } |
|
218 |
|
219 clientThread = PR_CreateThread(PR_USER_THREAD, |
|
220 clientThreadFunc, (void *) listenPort3, |
|
221 PR_PRIORITY_NORMAL, PR_GLOBAL_BOUND_THREAD, |
|
222 PR_UNJOINABLE_THREAD, 0); |
|
223 if (clientThread == NULL) { |
|
224 fprintf(stderr, "can't create thread\n"); |
|
225 exit(1); |
|
226 } |
|
227 |
|
228 |
|
229 printf("Three client threads are created. Each of them will\n"); |
|
230 printf("send data to one of the three ports the server is listening on.\n"); |
|
231 printf("The data they send is the port number. Each of them send\n"); |
|
232 printf("the data five times, so you should see ten lines below,\n"); |
|
233 printf("interleaved in an arbitrary order.\n"); |
|
234 |
|
235 /* 30 events total */ |
|
236 i = 0; |
|
237 while (i < 30) { |
|
238 PRPollDesc *tmp; |
|
239 int nextIndex; |
|
240 int nEvents = 0; |
|
241 |
|
242 retVal = PR_Poll(pds, npds, PR_INTERVAL_NO_TIMEOUT); |
|
243 PR_ASSERT(retVal != 0); /* no timeout */ |
|
244 if (retVal == -1) { |
|
245 fprintf(stderr, "PR_Poll failed\n"); |
|
246 exit(1); |
|
247 } |
|
248 |
|
249 nextIndex = 3; |
|
250 /* the three listening sockets */ |
|
251 for (j = 0; j < 3; j++) { |
|
252 other_pds[j] = pds[j]; |
|
253 PR_ASSERT((pds[j].out_flags & PR_POLL_WRITE) == 0 |
|
254 && (pds[j].out_flags & PR_POLL_EXCEPT) == 0); |
|
255 if (pds[j].out_flags & PR_POLL_READ) { |
|
256 PRFileDesc *sock; |
|
257 |
|
258 nEvents++; |
|
259 if (j == 2) { |
|
260 PROsfd newsd; |
|
261 newsd = accept(PR_FileDesc2NativeHandle(pds[j].fd), NULL, 0); |
|
262 if (newsd == -1) { |
|
263 fprintf(stderr, "accept() failed\n"); |
|
264 exit(1); |
|
265 } |
|
266 other_pds[nextIndex].fd = PR_CreateSocketPollFd(newsd); |
|
267 PR_ASSERT(other_pds[nextIndex].fd); |
|
268 other_pds[nextIndex].in_flags = PR_POLL_READ; |
|
269 } else { |
|
270 sock = PR_Accept(pds[j].fd, NULL, PR_INTERVAL_NO_TIMEOUT); |
|
271 if (sock == NULL) { |
|
272 fprintf(stderr, "PR_Accept() failed\n"); |
|
273 exit(1); |
|
274 } |
|
275 other_pds[nextIndex].fd = sock; |
|
276 other_pds[nextIndex].in_flags = PR_POLL_READ; |
|
277 } |
|
278 nextIndex++; |
|
279 } else if (pds[j].out_flags & PR_POLL_ERR) { |
|
280 fprintf(stderr, "PR_Poll() indicates that an fd has error\n"); |
|
281 exit(1); |
|
282 } else if (pds[j].out_flags & PR_POLL_NVAL) { |
|
283 fprintf(stderr, "PR_Poll() indicates that fd %d is invalid\n", |
|
284 PR_FileDesc2NativeHandle(pds[j].fd)); |
|
285 exit(1); |
|
286 } |
|
287 } |
|
288 |
|
289 for (j = 3; j < npds; j++) { |
|
290 PR_ASSERT((pds[j].out_flags & PR_POLL_WRITE) == 0 |
|
291 && (pds[j].out_flags & PR_POLL_EXCEPT) == 0); |
|
292 if (pds[j].out_flags & PR_POLL_READ) { |
|
293 PRInt32 nBytes; |
|
294 |
|
295 nEvents++; |
|
296 /* XXX: This call is a hack and should be fixed */ |
|
297 if (PR_GetDescType(pds[j].fd) == (PRDescType) 0) { |
|
298 nBytes = recv(PR_FileDesc2NativeHandle(pds[j].fd), buf, |
|
299 sizeof(buf), 0); |
|
300 if (nBytes == -1) { |
|
301 fprintf(stderr, "recv() failed\n"); |
|
302 exit(1); |
|
303 } |
|
304 printf("Server read %d bytes from native fd %d\n",nBytes, |
|
305 PR_FileDesc2NativeHandle(pds[j].fd)); |
|
306 #ifdef WIN32 |
|
307 closesocket((SOCKET)PR_FileDesc2NativeHandle(pds[j].fd)); |
|
308 #else |
|
309 close(PR_FileDesc2NativeHandle(pds[j].fd)); |
|
310 #endif |
|
311 PR_DestroySocketPollFd(pds[j].fd); |
|
312 } else { |
|
313 nBytes = PR_Read(pds[j].fd, buf, sizeof(buf)); |
|
314 if (nBytes == -1) { |
|
315 fprintf(stderr, "PR_Read() failed\n"); |
|
316 exit(1); |
|
317 } |
|
318 PR_Close(pds[j].fd); |
|
319 } |
|
320 /* Just to be safe */ |
|
321 buf[BUF_SIZE - 1] = '\0'; |
|
322 printf("The server received \"%s\" from a client\n", buf); |
|
323 } else if (pds[j].out_flags & PR_POLL_ERR) { |
|
324 fprintf(stderr, "PR_Poll() indicates that an fd has error\n"); |
|
325 exit(1); |
|
326 } else if (pds[j].out_flags & PR_POLL_NVAL) { |
|
327 fprintf(stderr, "PR_Poll() indicates that an fd is invalid\n"); |
|
328 exit(1); |
|
329 } else { |
|
330 other_pds[nextIndex] = pds[j]; |
|
331 nextIndex++; |
|
332 } |
|
333 } |
|
334 |
|
335 PR_ASSERT(retVal == nEvents); |
|
336 /* swap */ |
|
337 tmp = pds; |
|
338 pds = other_pds; |
|
339 other_pds = tmp; |
|
340 npds = nextIndex; |
|
341 i += nEvents; |
|
342 } |
|
343 PR_DestroySocketPollFd(socket_poll_fd); |
|
344 |
|
345 printf("All tests finished\n"); |
|
346 PR_Cleanup(); |
|
347 return 0; |
|
348 } |
|
349 |
|
350 |
|
351 #endif /* ifdef WINCE */ |