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