|
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 #include "prio.h" |
|
7 #include "prprf.h" |
|
8 #include "prlog.h" |
|
9 #include "prnetdb.h" |
|
10 #include "prthread.h" |
|
11 |
|
12 #include "plerror.h" |
|
13 #include "plgetopt.h" |
|
14 #include "prwin16.h" |
|
15 |
|
16 #include <stdlib.h> |
|
17 #include <string.h> |
|
18 |
|
19 /* |
|
20 ** Testing layering of I/O |
|
21 ** |
|
22 ** The layered server |
|
23 ** A thread that acts as a server. It creates a TCP listener with a dummy |
|
24 ** layer pushed on top. Then listens for incoming connections. Each connection |
|
25 ** request for connection will be layered as well, accept one request, echo |
|
26 ** it back and close. |
|
27 ** |
|
28 ** The layered client |
|
29 ** Pretty much what you'd expect. |
|
30 */ |
|
31 |
|
32 static PRFileDesc *logFile; |
|
33 static PRDescIdentity identity; |
|
34 static PRNetAddr server_address; |
|
35 |
|
36 static PRIOMethods myMethods; |
|
37 |
|
38 typedef enum Verbosity {silent, quiet, chatty, noisy} Verbosity; |
|
39 |
|
40 static PRIntn minor_iterations = 5; |
|
41 static PRIntn major_iterations = 1; |
|
42 static Verbosity verbosity = quiet; |
|
43 static PRUint16 default_port = 12273; |
|
44 |
|
45 static PRFileDesc *PushLayer(PRFileDesc *stack) |
|
46 { |
|
47 PRFileDesc *layer = PR_CreateIOLayerStub(identity, &myMethods); |
|
48 PRStatus rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), layer); |
|
49 if (verbosity > quiet) |
|
50 PR_fprintf(logFile, "Pushed layer(0x%x) onto stack(0x%x)\n", layer, stack); |
|
51 PR_ASSERT(PR_SUCCESS == rv); |
|
52 return stack; |
|
53 } /* PushLayer */ |
|
54 |
|
55 static PRFileDesc *PushNewLayers(PRFileDesc *stack) |
|
56 { |
|
57 PRDescIdentity tmp_identity; |
|
58 PRFileDesc *layer; |
|
59 PRStatus rv; |
|
60 |
|
61 /* push a dummy layer */ |
|
62 tmp_identity = PR_GetUniqueIdentity("Dummy 1"); |
|
63 layer = PR_CreateIOLayerStub(tmp_identity, PR_GetDefaultIOMethods()); |
|
64 rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), layer); |
|
65 if (verbosity > quiet) |
|
66 PR_fprintf(logFile, "Pushed layer(0x%x) onto stack(0x%x)\n", layer, |
|
67 stack); |
|
68 PR_ASSERT(PR_SUCCESS == rv); |
|
69 |
|
70 /* push a data procesing layer */ |
|
71 layer = PR_CreateIOLayerStub(identity, &myMethods); |
|
72 rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), layer); |
|
73 if (verbosity > quiet) |
|
74 PR_fprintf(logFile, "Pushed layer(0x%x) onto stack(0x%x)\n", layer, |
|
75 stack); |
|
76 PR_ASSERT(PR_SUCCESS == rv); |
|
77 |
|
78 /* push another dummy layer */ |
|
79 tmp_identity = PR_GetUniqueIdentity("Dummy 2"); |
|
80 layer = PR_CreateIOLayerStub(tmp_identity, PR_GetDefaultIOMethods()); |
|
81 rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), layer); |
|
82 if (verbosity > quiet) |
|
83 PR_fprintf(logFile, "Pushed layer(0x%x) onto stack(0x%x)\n", layer, |
|
84 stack); |
|
85 PR_ASSERT(PR_SUCCESS == rv); |
|
86 return stack; |
|
87 } /* PushLayer */ |
|
88 |
|
89 #if 0 |
|
90 static PRFileDesc *PopLayer(PRFileDesc *stack) |
|
91 { |
|
92 PRFileDesc *popped = PR_PopIOLayer(stack, identity); |
|
93 if (verbosity > quiet) |
|
94 PR_fprintf(logFile, "Popped layer(0x%x) from stack(0x%x)\n", popped, stack); |
|
95 popped->dtor(popped); |
|
96 |
|
97 return stack; |
|
98 } /* PopLayer */ |
|
99 #endif |
|
100 |
|
101 static void PR_CALLBACK Client(void *arg) |
|
102 { |
|
103 PRStatus rv; |
|
104 PRUint8 buffer[100]; |
|
105 PRIntn empty_flags = 0; |
|
106 PRIntn bytes_read, bytes_sent; |
|
107 PRFileDesc *stack = (PRFileDesc*)arg; |
|
108 |
|
109 /* Initialize the buffer so that Purify won't complain */ |
|
110 memset(buffer, 0, sizeof(buffer)); |
|
111 |
|
112 rv = PR_Connect(stack, &server_address, PR_INTERVAL_NO_TIMEOUT); |
|
113 PR_ASSERT(PR_SUCCESS == rv); |
|
114 while (minor_iterations-- > 0) |
|
115 { |
|
116 bytes_sent = PR_Send( |
|
117 stack, buffer, sizeof(buffer), empty_flags, PR_INTERVAL_NO_TIMEOUT); |
|
118 PR_ASSERT(sizeof(buffer) == bytes_sent); |
|
119 if (verbosity > chatty) |
|
120 PR_fprintf(logFile, "Client sending %d bytes\n", bytes_sent); |
|
121 bytes_read = PR_Recv( |
|
122 stack, buffer, bytes_sent, empty_flags, PR_INTERVAL_NO_TIMEOUT); |
|
123 if (verbosity > chatty) |
|
124 PR_fprintf(logFile, "Client receiving %d bytes\n", bytes_read); |
|
125 PR_ASSERT(bytes_read == bytes_sent); |
|
126 } |
|
127 |
|
128 if (verbosity > quiet) |
|
129 PR_fprintf(logFile, "Client shutting down stack\n"); |
|
130 |
|
131 rv = PR_Shutdown(stack, PR_SHUTDOWN_BOTH); PR_ASSERT(PR_SUCCESS == rv); |
|
132 } /* Client */ |
|
133 |
|
134 static void PR_CALLBACK Server(void *arg) |
|
135 { |
|
136 PRStatus rv; |
|
137 PRUint8 buffer[100]; |
|
138 PRFileDesc *service; |
|
139 PRUintn empty_flags = 0; |
|
140 PRIntn bytes_read, bytes_sent; |
|
141 PRFileDesc *stack = (PRFileDesc*)arg; |
|
142 PRNetAddr client_address; |
|
143 |
|
144 service = PR_Accept(stack, &client_address, PR_INTERVAL_NO_TIMEOUT); |
|
145 if (verbosity > quiet) |
|
146 PR_fprintf(logFile, "Server accepting connection\n"); |
|
147 |
|
148 do |
|
149 { |
|
150 bytes_read = PR_Recv( |
|
151 service, buffer, sizeof(buffer), empty_flags, PR_INTERVAL_NO_TIMEOUT); |
|
152 if (0 != bytes_read) |
|
153 { |
|
154 if (verbosity > chatty) |
|
155 PR_fprintf(logFile, "Server receiving %d bytes\n", bytes_read); |
|
156 PR_ASSERT(bytes_read > 0); |
|
157 bytes_sent = PR_Send( |
|
158 service, buffer, bytes_read, empty_flags, PR_INTERVAL_NO_TIMEOUT); |
|
159 if (verbosity > chatty) |
|
160 PR_fprintf(logFile, "Server sending %d bytes\n", bytes_sent); |
|
161 PR_ASSERT(bytes_read == bytes_sent); |
|
162 } |
|
163 |
|
164 } while (0 != bytes_read); |
|
165 |
|
166 if (verbosity > quiet) |
|
167 PR_fprintf(logFile, "Server shutting down and closing stack\n"); |
|
168 rv = PR_Shutdown(service, PR_SHUTDOWN_BOTH); PR_ASSERT(PR_SUCCESS == rv); |
|
169 rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv); |
|
170 |
|
171 } /* Server */ |
|
172 |
|
173 static PRInt32 PR_CALLBACK MyRecv( |
|
174 PRFileDesc *fd, void *buf, PRInt32 amount, |
|
175 PRIntn flags, PRIntervalTime timeout) |
|
176 { |
|
177 char *b = (char*)buf; |
|
178 PRFileDesc *lo = fd->lower; |
|
179 PRInt32 rv, readin = 0, request = 0; |
|
180 rv = lo->methods->recv(lo, &request, sizeof(request), flags, timeout); |
|
181 if (verbosity > chatty) PR_fprintf( |
|
182 logFile, "MyRecv sending permission for %d bytes\n", request); |
|
183 if (0 < rv) |
|
184 { |
|
185 if (verbosity > chatty) PR_fprintf( |
|
186 logFile, "MyRecv received permission request for %d bytes\n", request); |
|
187 rv = lo->methods->send( |
|
188 lo, &request, sizeof(request), flags, timeout); |
|
189 if (0 < rv) |
|
190 { |
|
191 if (verbosity > chatty) PR_fprintf( |
|
192 logFile, "MyRecv sending permission for %d bytes\n", request); |
|
193 while (readin < request) |
|
194 { |
|
195 rv = lo->methods->recv( |
|
196 lo, b + readin, amount - readin, flags, timeout); |
|
197 if (rv <= 0) break; |
|
198 if (verbosity > chatty) PR_fprintf( |
|
199 logFile, "MyRecv received %d bytes\n", rv); |
|
200 readin += rv; |
|
201 } |
|
202 rv = readin; |
|
203 } |
|
204 } |
|
205 return rv; |
|
206 } /* MyRecv */ |
|
207 |
|
208 static PRInt32 PR_CALLBACK MySend( |
|
209 PRFileDesc *fd, const void *buf, PRInt32 amount, |
|
210 PRIntn flags, PRIntervalTime timeout) |
|
211 { |
|
212 PRFileDesc *lo = fd->lower; |
|
213 const char *b = (const char*)buf; |
|
214 PRInt32 rv, wroteout = 0, request; |
|
215 if (verbosity > chatty) PR_fprintf( |
|
216 logFile, "MySend asking permission to send %d bytes\n", amount); |
|
217 rv = lo->methods->send(lo, &amount, sizeof(amount), flags, timeout); |
|
218 if (0 < rv) |
|
219 { |
|
220 rv = lo->methods->recv( |
|
221 lo, &request, sizeof(request), flags, timeout); |
|
222 if (0 < rv) |
|
223 { |
|
224 PR_ASSERT(request == amount); |
|
225 if (verbosity > chatty) PR_fprintf( |
|
226 logFile, "MySend got permission to send %d bytes\n", request); |
|
227 while (wroteout < request) |
|
228 { |
|
229 rv = lo->methods->send( |
|
230 lo, b + wroteout, request - wroteout, flags, timeout); |
|
231 if (rv <= 0) break; |
|
232 if (verbosity > chatty) PR_fprintf( |
|
233 logFile, "MySend wrote %d bytes\n", rv); |
|
234 wroteout += rv; |
|
235 } |
|
236 rv = amount; |
|
237 } |
|
238 } |
|
239 return rv; |
|
240 } /* MySend */ |
|
241 |
|
242 static Verbosity ChangeVerbosity(Verbosity verbosity, PRIntn delta) |
|
243 { |
|
244 PRIntn verbage = (PRIntn)verbosity + delta; |
|
245 if (verbage < (PRIntn)silent) verbage = (PRIntn)silent; |
|
246 else if (verbage > (PRIntn)noisy) verbage = (PRIntn)noisy; |
|
247 return (Verbosity)verbage; |
|
248 } /* ChangeVerbosity */ |
|
249 |
|
250 int main(int argc, char **argv) |
|
251 { |
|
252 PRStatus rv; |
|
253 PRIntn mits; |
|
254 PLOptStatus os; |
|
255 PRFileDesc *client, *service; |
|
256 PRFileDesc *client_stack, *service_stack; |
|
257 PRNetAddr any_address; |
|
258 const char *server_name = NULL; |
|
259 const PRIOMethods *stubMethods; |
|
260 PRThread *client_thread, *server_thread; |
|
261 PRThreadScope thread_scope = PR_LOCAL_THREAD; |
|
262 PLOptState *opt = PL_CreateOptState(argc, argv, "dqGC:c:p:"); |
|
263 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) |
|
264 { |
|
265 if (PL_OPT_BAD == os) continue; |
|
266 switch (opt->option) |
|
267 { |
|
268 case 0: |
|
269 server_name = opt->value; |
|
270 break; |
|
271 case 'd': /* debug mode */ |
|
272 if (verbosity < noisy) |
|
273 verbosity = ChangeVerbosity(verbosity, 1); |
|
274 break; |
|
275 case 'q': /* debug mode */ |
|
276 if (verbosity > silent) |
|
277 verbosity = ChangeVerbosity(verbosity, -1); |
|
278 break; |
|
279 case 'G': /* use global threads */ |
|
280 thread_scope = PR_GLOBAL_THREAD; |
|
281 break; |
|
282 case 'C': /* number of threads waiting */ |
|
283 major_iterations = atoi(opt->value); |
|
284 break; |
|
285 case 'c': /* number of client threads */ |
|
286 minor_iterations = atoi(opt->value); |
|
287 break; |
|
288 case 'p': /* default port */ |
|
289 default_port = atoi(opt->value); |
|
290 break; |
|
291 default: |
|
292 break; |
|
293 } |
|
294 } |
|
295 PL_DestroyOptState(opt); |
|
296 PR_STDIO_INIT(); |
|
297 |
|
298 logFile = PR_GetSpecialFD(PR_StandardError); |
|
299 |
|
300 identity = PR_GetUniqueIdentity("Dummy"); |
|
301 stubMethods = PR_GetDefaultIOMethods(); |
|
302 |
|
303 /* |
|
304 ** The protocol we're going to implement is one where in order to initiate |
|
305 ** a send, the sender must first solicit permission. Therefore, every |
|
306 ** send is really a send - receive - send sequence. |
|
307 */ |
|
308 myMethods = *stubMethods; /* first get the entire batch */ |
|
309 myMethods.recv = MyRecv; /* then override the ones we care about */ |
|
310 myMethods.send = MySend; /* then override the ones we care about */ |
|
311 |
|
312 if (NULL == server_name) |
|
313 rv = PR_InitializeNetAddr( |
|
314 PR_IpAddrLoopback, default_port, &server_address); |
|
315 else |
|
316 { |
|
317 rv = PR_StringToNetAddr(server_name, &server_address); |
|
318 PR_ASSERT(PR_SUCCESS == rv); |
|
319 rv = PR_InitializeNetAddr( |
|
320 PR_IpAddrNull, default_port, &server_address); |
|
321 } |
|
322 PR_ASSERT(PR_SUCCESS == rv); |
|
323 |
|
324 /* one type w/o layering */ |
|
325 |
|
326 mits = minor_iterations; |
|
327 while (major_iterations-- > 0) |
|
328 { |
|
329 if (verbosity > silent) |
|
330 PR_fprintf(logFile, "Beginning non-layered test\n"); |
|
331 client = PR_NewTCPSocket(); PR_ASSERT(NULL != client); |
|
332 service = PR_NewTCPSocket(); PR_ASSERT(NULL != service); |
|
333 rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &any_address); |
|
334 PR_ASSERT(PR_SUCCESS == rv); |
|
335 rv = PR_Bind(service, &any_address); PR_ASSERT(PR_SUCCESS == rv); |
|
336 rv = PR_Listen(service, 10); PR_ASSERT(PR_SUCCESS == rv); |
|
337 |
|
338 minor_iterations = mits; |
|
339 server_thread = PR_CreateThread( |
|
340 PR_USER_THREAD, Server, service, |
|
341 PR_PRIORITY_HIGH, thread_scope, |
|
342 PR_JOINABLE_THREAD, 16 * 1024); |
|
343 PR_ASSERT(NULL != server_thread); |
|
344 |
|
345 client_thread = PR_CreateThread( |
|
346 PR_USER_THREAD, Client, client, |
|
347 PR_PRIORITY_NORMAL, thread_scope, |
|
348 PR_JOINABLE_THREAD, 16 * 1024); |
|
349 PR_ASSERT(NULL != client_thread); |
|
350 |
|
351 rv = PR_JoinThread(client_thread); |
|
352 PR_ASSERT(PR_SUCCESS == rv); |
|
353 rv = PR_JoinThread(server_thread); |
|
354 PR_ASSERT(PR_SUCCESS == rv); |
|
355 |
|
356 rv = PR_Close(client); PR_ASSERT(PR_SUCCESS == rv); |
|
357 rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv); |
|
358 if (verbosity > silent) |
|
359 PR_fprintf(logFile, "Ending non-layered test\n"); |
|
360 |
|
361 /* with layering */ |
|
362 if (verbosity > silent) |
|
363 PR_fprintf(logFile, "Beginning layered test\n"); |
|
364 client = PR_NewTCPSocket(); PR_ASSERT(NULL != client); |
|
365 PushLayer(client); |
|
366 service = PR_NewTCPSocket(); PR_ASSERT(NULL != service); |
|
367 PushLayer(service); |
|
368 rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &any_address); |
|
369 PR_ASSERT(PR_SUCCESS == rv); |
|
370 rv = PR_Bind(service, &any_address); PR_ASSERT(PR_SUCCESS == rv); |
|
371 rv = PR_Listen(service, 10); PR_ASSERT(PR_SUCCESS == rv); |
|
372 |
|
373 minor_iterations = mits; |
|
374 server_thread = PR_CreateThread( |
|
375 PR_USER_THREAD, Server, service, |
|
376 PR_PRIORITY_HIGH, thread_scope, |
|
377 PR_JOINABLE_THREAD, 16 * 1024); |
|
378 PR_ASSERT(NULL != server_thread); |
|
379 |
|
380 client_thread = PR_CreateThread( |
|
381 PR_USER_THREAD, Client, client, |
|
382 PR_PRIORITY_NORMAL, thread_scope, |
|
383 PR_JOINABLE_THREAD, 16 * 1024); |
|
384 PR_ASSERT(NULL != client_thread); |
|
385 |
|
386 rv = PR_JoinThread(client_thread); |
|
387 PR_ASSERT(PR_SUCCESS == rv); |
|
388 rv = PR_JoinThread(server_thread); |
|
389 PR_ASSERT(PR_SUCCESS == rv); |
|
390 |
|
391 rv = PR_Close(client); PR_ASSERT(PR_SUCCESS == rv); |
|
392 rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv); |
|
393 /* with layering, using new style stack */ |
|
394 if (verbosity > silent) |
|
395 PR_fprintf(logFile, |
|
396 "Beginning layered test with new style stack\n"); |
|
397 client = PR_NewTCPSocket(); PR_ASSERT(NULL != client); |
|
398 client_stack = PR_CreateIOLayer(client); |
|
399 PushNewLayers(client_stack); |
|
400 service = PR_NewTCPSocket(); PR_ASSERT(NULL != service); |
|
401 service_stack = PR_CreateIOLayer(service); |
|
402 PushNewLayers(service_stack); |
|
403 rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &any_address); |
|
404 PR_ASSERT(PR_SUCCESS == rv); |
|
405 rv = PR_Bind(service, &any_address); PR_ASSERT(PR_SUCCESS == rv); |
|
406 rv = PR_Listen(service, 10); PR_ASSERT(PR_SUCCESS == rv); |
|
407 |
|
408 minor_iterations = mits; |
|
409 server_thread = PR_CreateThread( |
|
410 PR_USER_THREAD, Server, service_stack, |
|
411 PR_PRIORITY_HIGH, thread_scope, |
|
412 PR_JOINABLE_THREAD, 16 * 1024); |
|
413 PR_ASSERT(NULL != server_thread); |
|
414 |
|
415 client_thread = PR_CreateThread( |
|
416 PR_USER_THREAD, Client, client_stack, |
|
417 PR_PRIORITY_NORMAL, thread_scope, |
|
418 PR_JOINABLE_THREAD, 16 * 1024); |
|
419 PR_ASSERT(NULL != client_thread); |
|
420 |
|
421 rv = PR_JoinThread(client_thread); |
|
422 PR_ASSERT(PR_SUCCESS == rv); |
|
423 rv = PR_JoinThread(server_thread); |
|
424 PR_ASSERT(PR_SUCCESS == rv); |
|
425 |
|
426 rv = PR_Close(client_stack); PR_ASSERT(PR_SUCCESS == rv); |
|
427 rv = PR_Close(service_stack); PR_ASSERT(PR_SUCCESS == rv); |
|
428 if (verbosity > silent) |
|
429 PR_fprintf(logFile, "Ending layered test\n"); |
|
430 } |
|
431 return 0; |
|
432 } /* main */ |
|
433 |
|
434 /* layer.c */ |