Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "prio.h" |
michael@0 | 7 | #include "prmem.h" |
michael@0 | 8 | #include "prprf.h" |
michael@0 | 9 | #include "prlog.h" |
michael@0 | 10 | #include "prerror.h" |
michael@0 | 11 | #include "prnetdb.h" |
michael@0 | 12 | #include "prthread.h" |
michael@0 | 13 | |
michael@0 | 14 | #include "plerror.h" |
michael@0 | 15 | #include "plgetopt.h" |
michael@0 | 16 | #include "prwin16.h" |
michael@0 | 17 | |
michael@0 | 18 | #include <stdlib.h> |
michael@0 | 19 | #include <string.h> |
michael@0 | 20 | |
michael@0 | 21 | /* |
michael@0 | 22 | ** Testing layering of I/O |
michael@0 | 23 | ** |
michael@0 | 24 | ** The layered server |
michael@0 | 25 | ** A thread that acts as a server. It creates a TCP listener with a dummy |
michael@0 | 26 | ** layer pushed on top. Then listens for incoming connections. Each connection |
michael@0 | 27 | ** request for connection will be layered as well, accept one request, echo |
michael@0 | 28 | ** it back and close. |
michael@0 | 29 | ** |
michael@0 | 30 | ** The layered client |
michael@0 | 31 | ** Pretty much what you'd expect. |
michael@0 | 32 | */ |
michael@0 | 33 | |
michael@0 | 34 | static PRFileDesc *logFile; |
michael@0 | 35 | static PRDescIdentity identity; |
michael@0 | 36 | static PRNetAddr server_address; |
michael@0 | 37 | |
michael@0 | 38 | static PRIOMethods myMethods; |
michael@0 | 39 | |
michael@0 | 40 | typedef enum {rcv_get_debit, rcv_send_credit, rcv_data} RcvState; |
michael@0 | 41 | typedef enum {xmt_send_debit, xmt_recv_credit, xmt_data} XmtState; |
michael@0 | 42 | |
michael@0 | 43 | struct PRFilePrivate |
michael@0 | 44 | { |
michael@0 | 45 | RcvState rcvstate; |
michael@0 | 46 | XmtState xmtstate; |
michael@0 | 47 | PRInt32 rcvreq, rcvinprogress; |
michael@0 | 48 | PRInt32 xmtreq, xmtinprogress; |
michael@0 | 49 | }; |
michael@0 | 50 | |
michael@0 | 51 | typedef enum Verbosity {silent, quiet, chatty, noisy} Verbosity; |
michael@0 | 52 | |
michael@0 | 53 | static PRIntn minor_iterations = 5; |
michael@0 | 54 | static PRIntn major_iterations = 1; |
michael@0 | 55 | static Verbosity verbosity = quiet; |
michael@0 | 56 | static PRUint16 default_port = 12273; |
michael@0 | 57 | |
michael@0 | 58 | static PRFileDesc *PushLayer(PRFileDesc *stack) |
michael@0 | 59 | { |
michael@0 | 60 | PRStatus rv; |
michael@0 | 61 | PRFileDesc *layer = PR_CreateIOLayerStub(identity, &myMethods); |
michael@0 | 62 | layer->secret = PR_NEWZAP(PRFilePrivate); |
michael@0 | 63 | rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), layer); |
michael@0 | 64 | PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 65 | if (verbosity > quiet) |
michael@0 | 66 | PR_fprintf(logFile, "Pushed layer(0x%x) onto stack(0x%x)\n", layer, stack); |
michael@0 | 67 | return stack; |
michael@0 | 68 | } /* PushLayer */ |
michael@0 | 69 | |
michael@0 | 70 | static PRFileDesc *PopLayer(PRFileDesc *stack) |
michael@0 | 71 | { |
michael@0 | 72 | PRFileDesc *popped = PR_PopIOLayer(stack, identity); |
michael@0 | 73 | if (verbosity > quiet) |
michael@0 | 74 | PR_fprintf(logFile, "Popped layer(0x%x) from stack(0x%x)\n", popped, stack); |
michael@0 | 75 | PR_DELETE(popped->secret); |
michael@0 | 76 | popped->dtor(popped); |
michael@0 | 77 | return stack; |
michael@0 | 78 | } /* PopLayer */ |
michael@0 | 79 | |
michael@0 | 80 | static void PR_CALLBACK Client(void *arg) |
michael@0 | 81 | { |
michael@0 | 82 | PRStatus rv; |
michael@0 | 83 | PRIntn mits; |
michael@0 | 84 | PRInt32 ready; |
michael@0 | 85 | PRUint8 buffer[100]; |
michael@0 | 86 | PRPollDesc polldesc; |
michael@0 | 87 | PRIntn empty_flags = 0; |
michael@0 | 88 | PRIntn bytes_read, bytes_sent; |
michael@0 | 89 | PRFileDesc *stack = (PRFileDesc*)arg; |
michael@0 | 90 | |
michael@0 | 91 | /* Initialize the buffer so that Purify won't complain */ |
michael@0 | 92 | memset(buffer, 0, sizeof(buffer)); |
michael@0 | 93 | |
michael@0 | 94 | rv = PR_Connect(stack, &server_address, PR_INTERVAL_NO_TIMEOUT); |
michael@0 | 95 | if ((PR_FAILURE == rv) && (PR_IN_PROGRESS_ERROR == PR_GetError())) |
michael@0 | 96 | { |
michael@0 | 97 | if (verbosity > quiet) |
michael@0 | 98 | PR_fprintf(logFile, "Client connect 'in progress'\n"); |
michael@0 | 99 | do |
michael@0 | 100 | { |
michael@0 | 101 | polldesc.fd = stack; |
michael@0 | 102 | polldesc.out_flags = 0; |
michael@0 | 103 | polldesc.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT; |
michael@0 | 104 | ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT); |
michael@0 | 105 | if ((1 != ready) /* if not 1, then we're dead */ |
michael@0 | 106 | || (0 == (polldesc.in_flags & polldesc.out_flags))) |
michael@0 | 107 | { PR_ASSERT(!"Whoa!"); break; } |
michael@0 | 108 | if (verbosity > quiet) |
michael@0 | 109 | PR_fprintf( |
michael@0 | 110 | logFile, "Client connect 'in progress' [0x%x]\n", |
michael@0 | 111 | polldesc.out_flags); |
michael@0 | 112 | rv = PR_GetConnectStatus(&polldesc); |
michael@0 | 113 | if ((PR_FAILURE == rv) |
michael@0 | 114 | && (PR_IN_PROGRESS_ERROR != PR_GetError())) break; |
michael@0 | 115 | } while (PR_FAILURE == rv); |
michael@0 | 116 | } |
michael@0 | 117 | PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 118 | if (verbosity > chatty) |
michael@0 | 119 | PR_fprintf(logFile, "Client created connection\n"); |
michael@0 | 120 | |
michael@0 | 121 | for (mits = 0; mits < minor_iterations; ++mits) |
michael@0 | 122 | { |
michael@0 | 123 | bytes_sent = 0; |
michael@0 | 124 | if (verbosity > quiet) |
michael@0 | 125 | PR_fprintf(logFile, "Client sending %d bytes\n", sizeof(buffer)); |
michael@0 | 126 | do |
michael@0 | 127 | { |
michael@0 | 128 | if (verbosity > chatty) |
michael@0 | 129 | PR_fprintf( |
michael@0 | 130 | logFile, "Client sending %d bytes\n", |
michael@0 | 131 | sizeof(buffer) - bytes_sent); |
michael@0 | 132 | ready = PR_Send( |
michael@0 | 133 | stack, buffer + bytes_sent, sizeof(buffer) - bytes_sent, |
michael@0 | 134 | empty_flags, PR_INTERVAL_NO_TIMEOUT); |
michael@0 | 135 | if (verbosity > chatty) |
michael@0 | 136 | PR_fprintf(logFile, "Client send status [%d]\n", ready); |
michael@0 | 137 | if (0 < ready) bytes_sent += ready; |
michael@0 | 138 | else if ((-1 == ready) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) |
michael@0 | 139 | { |
michael@0 | 140 | polldesc.fd = stack; |
michael@0 | 141 | polldesc.out_flags = 0; |
michael@0 | 142 | polldesc.in_flags = PR_POLL_WRITE; |
michael@0 | 143 | ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT); |
michael@0 | 144 | if ((1 != ready) /* if not 1, then we're dead */ |
michael@0 | 145 | || (0 == (polldesc.in_flags & polldesc.out_flags))) |
michael@0 | 146 | { PR_ASSERT(!"Whoa!"); break; } |
michael@0 | 147 | } |
michael@0 | 148 | else break; |
michael@0 | 149 | } while (bytes_sent < sizeof(buffer)); |
michael@0 | 150 | PR_ASSERT(sizeof(buffer) == bytes_sent); |
michael@0 | 151 | |
michael@0 | 152 | bytes_read = 0; |
michael@0 | 153 | do |
michael@0 | 154 | { |
michael@0 | 155 | if (verbosity > chatty) |
michael@0 | 156 | PR_fprintf( |
michael@0 | 157 | logFile, "Client receiving %d bytes\n", |
michael@0 | 158 | bytes_sent - bytes_read); |
michael@0 | 159 | ready = PR_Recv( |
michael@0 | 160 | stack, buffer + bytes_read, bytes_sent - bytes_read, |
michael@0 | 161 | empty_flags, PR_INTERVAL_NO_TIMEOUT); |
michael@0 | 162 | if (verbosity > chatty) |
michael@0 | 163 | PR_fprintf( |
michael@0 | 164 | logFile, "Client receive status [%d]\n", ready); |
michael@0 | 165 | if (0 < ready) bytes_read += ready; |
michael@0 | 166 | else if ((-1 == ready) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) |
michael@0 | 167 | { |
michael@0 | 168 | polldesc.fd = stack; |
michael@0 | 169 | polldesc.out_flags = 0; |
michael@0 | 170 | polldesc.in_flags = PR_POLL_READ; |
michael@0 | 171 | ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT); |
michael@0 | 172 | if ((1 != ready) /* if not 1, then we're dead */ |
michael@0 | 173 | || (0 == (polldesc.in_flags & polldesc.out_flags))) |
michael@0 | 174 | { PR_ASSERT(!"Whoa!"); break; } |
michael@0 | 175 | } |
michael@0 | 176 | else break; |
michael@0 | 177 | } while (bytes_read < bytes_sent); |
michael@0 | 178 | if (verbosity > chatty) |
michael@0 | 179 | PR_fprintf(logFile, "Client received %d bytes\n", bytes_read); |
michael@0 | 180 | PR_ASSERT(bytes_read == bytes_sent); |
michael@0 | 181 | } |
michael@0 | 182 | |
michael@0 | 183 | if (verbosity > quiet) |
michael@0 | 184 | PR_fprintf(logFile, "Client shutting down stack\n"); |
michael@0 | 185 | |
michael@0 | 186 | rv = PR_Shutdown(stack, PR_SHUTDOWN_BOTH); PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 187 | } /* Client */ |
michael@0 | 188 | |
michael@0 | 189 | static void PR_CALLBACK Server(void *arg) |
michael@0 | 190 | { |
michael@0 | 191 | PRStatus rv; |
michael@0 | 192 | PRInt32 ready; |
michael@0 | 193 | PRUint8 buffer[100]; |
michael@0 | 194 | PRFileDesc *service; |
michael@0 | 195 | PRUintn empty_flags = 0; |
michael@0 | 196 | struct PRPollDesc polldesc; |
michael@0 | 197 | PRIntn bytes_read, bytes_sent; |
michael@0 | 198 | PRFileDesc *stack = (PRFileDesc*)arg; |
michael@0 | 199 | PRNetAddr client_address; |
michael@0 | 200 | |
michael@0 | 201 | do |
michael@0 | 202 | { |
michael@0 | 203 | if (verbosity > chatty) |
michael@0 | 204 | PR_fprintf(logFile, "Server accepting connection\n"); |
michael@0 | 205 | service = PR_Accept(stack, &client_address, PR_INTERVAL_NO_TIMEOUT); |
michael@0 | 206 | if (verbosity > chatty) |
michael@0 | 207 | PR_fprintf(logFile, "Server accept status [0x%p]\n", service); |
michael@0 | 208 | if ((NULL == service) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) |
michael@0 | 209 | { |
michael@0 | 210 | polldesc.fd = stack; |
michael@0 | 211 | polldesc.out_flags = 0; |
michael@0 | 212 | polldesc.in_flags = PR_POLL_READ | PR_POLL_EXCEPT; |
michael@0 | 213 | ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT); |
michael@0 | 214 | if ((1 != ready) /* if not 1, then we're dead */ |
michael@0 | 215 | || (0 == (polldesc.in_flags & polldesc.out_flags))) |
michael@0 | 216 | { PR_ASSERT(!"Whoa!"); break; } |
michael@0 | 217 | } |
michael@0 | 218 | } while (NULL == service); |
michael@0 | 219 | PR_ASSERT(NULL != service); |
michael@0 | 220 | |
michael@0 | 221 | if (verbosity > quiet) |
michael@0 | 222 | PR_fprintf(logFile, "Server accepting connection\n"); |
michael@0 | 223 | |
michael@0 | 224 | do |
michael@0 | 225 | { |
michael@0 | 226 | bytes_read = 0; |
michael@0 | 227 | do |
michael@0 | 228 | { |
michael@0 | 229 | if (verbosity > chatty) |
michael@0 | 230 | PR_fprintf( |
michael@0 | 231 | logFile, "Server receiving %d bytes\n", |
michael@0 | 232 | sizeof(buffer) - bytes_read); |
michael@0 | 233 | ready = PR_Recv( |
michael@0 | 234 | service, buffer + bytes_read, sizeof(buffer) - bytes_read, |
michael@0 | 235 | empty_flags, PR_INTERVAL_NO_TIMEOUT); |
michael@0 | 236 | if (verbosity > chatty) |
michael@0 | 237 | PR_fprintf(logFile, "Server receive status [%d]\n", ready); |
michael@0 | 238 | if (0 < ready) bytes_read += ready; |
michael@0 | 239 | else if ((-1 == ready) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) |
michael@0 | 240 | { |
michael@0 | 241 | polldesc.fd = service; |
michael@0 | 242 | polldesc.out_flags = 0; |
michael@0 | 243 | polldesc.in_flags = PR_POLL_READ; |
michael@0 | 244 | ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT); |
michael@0 | 245 | if ((1 != ready) /* if not 1, then we're dead */ |
michael@0 | 246 | || (0 == (polldesc.in_flags & polldesc.out_flags))) |
michael@0 | 247 | { PR_ASSERT(!"Whoa!"); break; } |
michael@0 | 248 | } |
michael@0 | 249 | else break; |
michael@0 | 250 | } while (bytes_read < sizeof(buffer)); |
michael@0 | 251 | |
michael@0 | 252 | if (0 != bytes_read) |
michael@0 | 253 | { |
michael@0 | 254 | if (verbosity > chatty) |
michael@0 | 255 | PR_fprintf(logFile, "Server received %d bytes\n", bytes_read); |
michael@0 | 256 | PR_ASSERT(bytes_read > 0); |
michael@0 | 257 | |
michael@0 | 258 | bytes_sent = 0; |
michael@0 | 259 | do |
michael@0 | 260 | { |
michael@0 | 261 | ready = PR_Send( |
michael@0 | 262 | service, buffer + bytes_sent, bytes_read - bytes_sent, |
michael@0 | 263 | empty_flags, PR_INTERVAL_NO_TIMEOUT); |
michael@0 | 264 | if (0 < ready) |
michael@0 | 265 | { |
michael@0 | 266 | bytes_sent += ready; |
michael@0 | 267 | } |
michael@0 | 268 | else if ((-1 == ready) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) |
michael@0 | 269 | { |
michael@0 | 270 | polldesc.fd = service; |
michael@0 | 271 | polldesc.out_flags = 0; |
michael@0 | 272 | polldesc.in_flags = PR_POLL_WRITE; |
michael@0 | 273 | ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT); |
michael@0 | 274 | if ((1 != ready) /* if not 1, then we're dead */ |
michael@0 | 275 | || (0 == (polldesc.in_flags & polldesc.out_flags))) |
michael@0 | 276 | { PR_ASSERT(!"Whoa!"); break; } |
michael@0 | 277 | } |
michael@0 | 278 | else break; |
michael@0 | 279 | } while (bytes_sent < bytes_read); |
michael@0 | 280 | PR_ASSERT(bytes_read == bytes_sent); |
michael@0 | 281 | if (verbosity > chatty) |
michael@0 | 282 | PR_fprintf(logFile, "Server sent %d bytes\n", bytes_sent); |
michael@0 | 283 | } |
michael@0 | 284 | } while (0 != bytes_read); |
michael@0 | 285 | |
michael@0 | 286 | if (verbosity > quiet) |
michael@0 | 287 | PR_fprintf(logFile, "Server shutting down stack\n"); |
michael@0 | 288 | rv = PR_Shutdown(service, PR_SHUTDOWN_BOTH); PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 289 | rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 290 | |
michael@0 | 291 | } /* Server */ |
michael@0 | 292 | |
michael@0 | 293 | static PRStatus PR_CALLBACK MyClose(PRFileDesc *fd) |
michael@0 | 294 | { |
michael@0 | 295 | PR_DELETE(fd->secret); /* manage my secret file object */ |
michael@0 | 296 | return (PR_GetDefaultIOMethods())->close(fd); /* let him do all the work */ |
michael@0 | 297 | } /* MyClose */ |
michael@0 | 298 | |
michael@0 | 299 | static PRInt16 PR_CALLBACK MyPoll( |
michael@0 | 300 | PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags) |
michael@0 | 301 | { |
michael@0 | 302 | PRInt16 my_flags, new_flags; |
michael@0 | 303 | PRFilePrivate *mine = (PRFilePrivate*)fd->secret; |
michael@0 | 304 | if (0 != (PR_POLL_READ & in_flags)) |
michael@0 | 305 | { |
michael@0 | 306 | /* client thinks he's reading */ |
michael@0 | 307 | switch (mine->rcvstate) |
michael@0 | 308 | { |
michael@0 | 309 | case rcv_send_credit: |
michael@0 | 310 | my_flags = (in_flags & ~PR_POLL_READ) | PR_POLL_WRITE; |
michael@0 | 311 | break; |
michael@0 | 312 | case rcv_data: |
michael@0 | 313 | case rcv_get_debit: |
michael@0 | 314 | my_flags = in_flags; |
michael@0 | 315 | default: break; |
michael@0 | 316 | } |
michael@0 | 317 | } |
michael@0 | 318 | else if (0 != (PR_POLL_WRITE & in_flags)) |
michael@0 | 319 | { |
michael@0 | 320 | /* client thinks he's writing */ |
michael@0 | 321 | switch (mine->xmtstate) |
michael@0 | 322 | { |
michael@0 | 323 | case xmt_recv_credit: |
michael@0 | 324 | my_flags = (in_flags & ~PR_POLL_WRITE) | PR_POLL_READ; |
michael@0 | 325 | break; |
michael@0 | 326 | case xmt_send_debit: |
michael@0 | 327 | case xmt_data: |
michael@0 | 328 | my_flags = in_flags; |
michael@0 | 329 | default: break; |
michael@0 | 330 | } |
michael@0 | 331 | } |
michael@0 | 332 | else PR_ASSERT(!"How'd I get here?"); |
michael@0 | 333 | new_flags = (fd->lower->methods->poll)(fd->lower, my_flags, out_flags); |
michael@0 | 334 | if (verbosity > chatty) |
michael@0 | 335 | PR_fprintf( |
michael@0 | 336 | logFile, "Poll [i: 0x%x, m: 0x%x, o: 0x%x, n: 0x%x]\n", |
michael@0 | 337 | in_flags, my_flags, *out_flags, new_flags); |
michael@0 | 338 | return new_flags; |
michael@0 | 339 | } /* MyPoll */ |
michael@0 | 340 | |
michael@0 | 341 | static PRFileDesc * PR_CALLBACK MyAccept( |
michael@0 | 342 | PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout) |
michael@0 | 343 | { |
michael@0 | 344 | PRStatus rv; |
michael@0 | 345 | PRFileDesc *newfd, *layer = fd; |
michael@0 | 346 | PRFileDesc *newstack; |
michael@0 | 347 | PRFilePrivate *newsecret; |
michael@0 | 348 | |
michael@0 | 349 | PR_ASSERT(fd != NULL); |
michael@0 | 350 | PR_ASSERT(fd->lower != NULL); |
michael@0 | 351 | |
michael@0 | 352 | newstack = PR_NEW(PRFileDesc); |
michael@0 | 353 | if (NULL == newstack) |
michael@0 | 354 | { |
michael@0 | 355 | PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); |
michael@0 | 356 | return NULL; |
michael@0 | 357 | } |
michael@0 | 358 | newsecret = PR_NEW(PRFilePrivate); |
michael@0 | 359 | if (NULL == newsecret) |
michael@0 | 360 | { |
michael@0 | 361 | PR_DELETE(newstack); |
michael@0 | 362 | PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); |
michael@0 | 363 | return NULL; |
michael@0 | 364 | } |
michael@0 | 365 | *newstack = *fd; /* make a copy of the accepting layer */ |
michael@0 | 366 | *newsecret = *fd->secret; |
michael@0 | 367 | newstack->secret = newsecret; |
michael@0 | 368 | |
michael@0 | 369 | newfd = (fd->lower->methods->accept)(fd->lower, addr, timeout); |
michael@0 | 370 | if (NULL == newfd) |
michael@0 | 371 | { |
michael@0 | 372 | PR_DELETE(newsecret); |
michael@0 | 373 | PR_DELETE(newstack); |
michael@0 | 374 | return NULL; |
michael@0 | 375 | } |
michael@0 | 376 | |
michael@0 | 377 | /* this PR_PushIOLayer call cannot fail */ |
michael@0 | 378 | rv = PR_PushIOLayer(newfd, PR_TOP_IO_LAYER, newstack); |
michael@0 | 379 | PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 380 | return newfd; /* that's it */ |
michael@0 | 381 | } |
michael@0 | 382 | |
michael@0 | 383 | static PRInt32 PR_CALLBACK MyRecv( |
michael@0 | 384 | PRFileDesc *fd, void *buf, PRInt32 amount, |
michael@0 | 385 | PRIntn flags, PRIntervalTime timeout) |
michael@0 | 386 | { |
michael@0 | 387 | char *b; |
michael@0 | 388 | PRInt32 rv; |
michael@0 | 389 | PRFileDesc *lo = fd->lower; |
michael@0 | 390 | PRFilePrivate *mine = (PRFilePrivate*)fd->secret; |
michael@0 | 391 | |
michael@0 | 392 | do |
michael@0 | 393 | { |
michael@0 | 394 | switch (mine->rcvstate) |
michael@0 | 395 | { |
michael@0 | 396 | case rcv_get_debit: |
michael@0 | 397 | b = (char*)&mine->rcvreq; |
michael@0 | 398 | mine->rcvreq = amount; |
michael@0 | 399 | rv = lo->methods->recv( |
michael@0 | 400 | lo, b + mine->rcvinprogress, |
michael@0 | 401 | sizeof(mine->rcvreq) - mine->rcvinprogress, flags, timeout); |
michael@0 | 402 | if (0 == rv) goto closed; |
michael@0 | 403 | if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) break; |
michael@0 | 404 | mine->rcvinprogress += rv; /* accumulate the read */ |
michael@0 | 405 | if (mine->rcvinprogress < sizeof(mine->rcvreq)) break; /* loop */ |
michael@0 | 406 | mine->rcvstate = rcv_send_credit; |
michael@0 | 407 | mine->rcvinprogress = 0; |
michael@0 | 408 | case rcv_send_credit: |
michael@0 | 409 | b = (char*)&mine->rcvreq; |
michael@0 | 410 | rv = lo->methods->send( |
michael@0 | 411 | lo, b + mine->rcvinprogress, |
michael@0 | 412 | sizeof(mine->rcvreq) - mine->rcvinprogress, flags, timeout); |
michael@0 | 413 | if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) break; |
michael@0 | 414 | mine->rcvinprogress += rv; /* accumulate the read */ |
michael@0 | 415 | if (mine->rcvinprogress < sizeof(mine->rcvreq)) break; /* loop */ |
michael@0 | 416 | mine->rcvstate = rcv_data; |
michael@0 | 417 | mine->rcvinprogress = 0; |
michael@0 | 418 | case rcv_data: |
michael@0 | 419 | b = (char*)buf; |
michael@0 | 420 | rv = lo->methods->recv( |
michael@0 | 421 | lo, b + mine->rcvinprogress, |
michael@0 | 422 | mine->rcvreq - mine->rcvinprogress, flags, timeout); |
michael@0 | 423 | if (0 == rv) goto closed; |
michael@0 | 424 | if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) break; |
michael@0 | 425 | mine->rcvinprogress += rv; /* accumulate the read */ |
michael@0 | 426 | if (mine->rcvinprogress < amount) break; /* loop */ |
michael@0 | 427 | mine->rcvstate = rcv_get_debit; |
michael@0 | 428 | mine->rcvinprogress = 0; |
michael@0 | 429 | return mine->rcvreq; /* << -- that's it! */ |
michael@0 | 430 | default: |
michael@0 | 431 | break; |
michael@0 | 432 | } |
michael@0 | 433 | } while (-1 != rv); |
michael@0 | 434 | return rv; |
michael@0 | 435 | closed: |
michael@0 | 436 | mine->rcvinprogress = 0; |
michael@0 | 437 | mine->rcvstate = rcv_get_debit; |
michael@0 | 438 | return 0; |
michael@0 | 439 | } /* MyRecv */ |
michael@0 | 440 | |
michael@0 | 441 | static PRInt32 PR_CALLBACK MySend( |
michael@0 | 442 | PRFileDesc *fd, const void *buf, PRInt32 amount, |
michael@0 | 443 | PRIntn flags, PRIntervalTime timeout) |
michael@0 | 444 | { |
michael@0 | 445 | char *b; |
michael@0 | 446 | PRInt32 rv; |
michael@0 | 447 | PRFileDesc *lo = fd->lower; |
michael@0 | 448 | PRFilePrivate *mine = (PRFilePrivate*)fd->secret; |
michael@0 | 449 | |
michael@0 | 450 | do |
michael@0 | 451 | { |
michael@0 | 452 | switch (mine->xmtstate) |
michael@0 | 453 | { |
michael@0 | 454 | case xmt_send_debit: |
michael@0 | 455 | b = (char*)&mine->xmtreq; |
michael@0 | 456 | mine->xmtreq = amount; |
michael@0 | 457 | rv = lo->methods->send( |
michael@0 | 458 | lo, b - mine->xmtinprogress, |
michael@0 | 459 | sizeof(mine->xmtreq) - mine->xmtinprogress, flags, timeout); |
michael@0 | 460 | if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) break; |
michael@0 | 461 | mine->xmtinprogress += rv; |
michael@0 | 462 | if (mine->xmtinprogress < sizeof(mine->xmtreq)) break; |
michael@0 | 463 | mine->xmtstate = xmt_recv_credit; |
michael@0 | 464 | mine->xmtinprogress = 0; |
michael@0 | 465 | case xmt_recv_credit: |
michael@0 | 466 | b = (char*)&mine->xmtreq; |
michael@0 | 467 | rv = lo->methods->recv( |
michael@0 | 468 | lo, b + mine->xmtinprogress, |
michael@0 | 469 | sizeof(mine->xmtreq) - mine->xmtinprogress, flags, timeout); |
michael@0 | 470 | if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) break; |
michael@0 | 471 | mine->xmtinprogress += rv; |
michael@0 | 472 | if (mine->xmtinprogress < sizeof(mine->xmtreq)) break; |
michael@0 | 473 | mine->xmtstate = xmt_data; |
michael@0 | 474 | mine->xmtinprogress = 0; |
michael@0 | 475 | case xmt_data: |
michael@0 | 476 | b = (char*)buf; |
michael@0 | 477 | rv = lo->methods->send( |
michael@0 | 478 | lo, b + mine->xmtinprogress, |
michael@0 | 479 | mine->xmtreq - mine->xmtinprogress, flags, timeout); |
michael@0 | 480 | if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) break; |
michael@0 | 481 | mine->xmtinprogress += rv; |
michael@0 | 482 | if (mine->xmtinprogress < amount) break; |
michael@0 | 483 | mine->xmtstate = xmt_send_debit; |
michael@0 | 484 | mine->xmtinprogress = 0; |
michael@0 | 485 | return mine->xmtreq; /* <<-- That's the one! */ |
michael@0 | 486 | default: |
michael@0 | 487 | break; |
michael@0 | 488 | } |
michael@0 | 489 | } while (-1 != rv); |
michael@0 | 490 | return rv; |
michael@0 | 491 | } /* MySend */ |
michael@0 | 492 | |
michael@0 | 493 | static Verbosity ChangeVerbosity(Verbosity verbosity, PRIntn delta) |
michael@0 | 494 | { |
michael@0 | 495 | PRIntn verbage = (PRIntn)verbosity + delta; |
michael@0 | 496 | if (verbage < (PRIntn)silent) verbage = (PRIntn)silent; |
michael@0 | 497 | else if (verbage > (PRIntn)noisy) verbage = (PRIntn)noisy; |
michael@0 | 498 | return (Verbosity)verbage; |
michael@0 | 499 | } /* ChangeVerbosity */ |
michael@0 | 500 | |
michael@0 | 501 | int main(int argc, char **argv) |
michael@0 | 502 | { |
michael@0 | 503 | PRStatus rv; |
michael@0 | 504 | PLOptStatus os; |
michael@0 | 505 | PRFileDesc *client, *service; |
michael@0 | 506 | PRNetAddr any_address; |
michael@0 | 507 | const char *server_name = NULL; |
michael@0 | 508 | const PRIOMethods *stubMethods; |
michael@0 | 509 | PRThread *client_thread, *server_thread; |
michael@0 | 510 | PRThreadScope thread_scope = PR_LOCAL_THREAD; |
michael@0 | 511 | PRSocketOptionData socket_noblock, socket_nodelay; |
michael@0 | 512 | PLOptState *opt = PL_CreateOptState(argc, argv, "dqGC:c:p:"); |
michael@0 | 513 | while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) |
michael@0 | 514 | { |
michael@0 | 515 | if (PL_OPT_BAD == os) continue; |
michael@0 | 516 | switch (opt->option) |
michael@0 | 517 | { |
michael@0 | 518 | case 0: |
michael@0 | 519 | server_name = opt->value; |
michael@0 | 520 | break; |
michael@0 | 521 | case 'd': /* debug mode */ |
michael@0 | 522 | if (verbosity < noisy) |
michael@0 | 523 | verbosity = ChangeVerbosity(verbosity, 1); |
michael@0 | 524 | break; |
michael@0 | 525 | case 'q': /* debug mode */ |
michael@0 | 526 | if (verbosity > silent) |
michael@0 | 527 | verbosity = ChangeVerbosity(verbosity, -1); |
michael@0 | 528 | break; |
michael@0 | 529 | case 'G': /* use global threads */ |
michael@0 | 530 | thread_scope = PR_GLOBAL_THREAD; |
michael@0 | 531 | break; |
michael@0 | 532 | case 'C': /* number of threads waiting */ |
michael@0 | 533 | major_iterations = atoi(opt->value); |
michael@0 | 534 | break; |
michael@0 | 535 | case 'c': /* number of client threads */ |
michael@0 | 536 | minor_iterations = atoi(opt->value); |
michael@0 | 537 | break; |
michael@0 | 538 | case 'p': /* default port */ |
michael@0 | 539 | default_port = atoi(opt->value); |
michael@0 | 540 | break; |
michael@0 | 541 | default: |
michael@0 | 542 | break; |
michael@0 | 543 | } |
michael@0 | 544 | } |
michael@0 | 545 | PL_DestroyOptState(opt); |
michael@0 | 546 | PR_STDIO_INIT(); |
michael@0 | 547 | |
michael@0 | 548 | logFile = PR_GetSpecialFD(PR_StandardError); |
michael@0 | 549 | identity = PR_GetUniqueIdentity("Dummy"); |
michael@0 | 550 | stubMethods = PR_GetDefaultIOMethods(); |
michael@0 | 551 | |
michael@0 | 552 | /* |
michael@0 | 553 | ** The protocol we're going to implement is one where in order to initiate |
michael@0 | 554 | ** a send, the sender must first solicit permission. Therefore, every |
michael@0 | 555 | ** send is really a send - receive - send sequence. |
michael@0 | 556 | */ |
michael@0 | 557 | myMethods = *stubMethods; /* first get the entire batch */ |
michael@0 | 558 | myMethods.accept = MyAccept; /* then override the ones we care about */ |
michael@0 | 559 | myMethods.recv = MyRecv; /* then override the ones we care about */ |
michael@0 | 560 | myMethods.send = MySend; /* then override the ones we care about */ |
michael@0 | 561 | myMethods.close = MyClose; /* then override the ones we care about */ |
michael@0 | 562 | myMethods.poll = MyPoll; /* then override the ones we care about */ |
michael@0 | 563 | |
michael@0 | 564 | if (NULL == server_name) |
michael@0 | 565 | rv = PR_InitializeNetAddr( |
michael@0 | 566 | PR_IpAddrLoopback, default_port, &server_address); |
michael@0 | 567 | else |
michael@0 | 568 | { |
michael@0 | 569 | rv = PR_StringToNetAddr(server_name, &server_address); |
michael@0 | 570 | PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 571 | rv = PR_InitializeNetAddr( |
michael@0 | 572 | PR_IpAddrNull, default_port, &server_address); |
michael@0 | 573 | } |
michael@0 | 574 | PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 575 | |
michael@0 | 576 | socket_noblock.value.non_blocking = PR_TRUE; |
michael@0 | 577 | socket_noblock.option = PR_SockOpt_Nonblocking; |
michael@0 | 578 | socket_nodelay.value.no_delay = PR_TRUE; |
michael@0 | 579 | socket_nodelay.option = PR_SockOpt_NoDelay; |
michael@0 | 580 | |
michael@0 | 581 | /* one type w/o layering */ |
michael@0 | 582 | |
michael@0 | 583 | while (major_iterations-- > 0) |
michael@0 | 584 | { |
michael@0 | 585 | if (verbosity > silent) |
michael@0 | 586 | PR_fprintf(logFile, "Beginning non-layered test\n"); |
michael@0 | 587 | |
michael@0 | 588 | client = PR_NewTCPSocket(); PR_ASSERT(NULL != client); |
michael@0 | 589 | service = PR_NewTCPSocket(); PR_ASSERT(NULL != service); |
michael@0 | 590 | |
michael@0 | 591 | rv = PR_SetSocketOption(client, &socket_noblock); |
michael@0 | 592 | PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 593 | rv = PR_SetSocketOption(service, &socket_noblock); |
michael@0 | 594 | PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 595 | rv = PR_SetSocketOption(client, &socket_nodelay); |
michael@0 | 596 | PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 597 | rv = PR_SetSocketOption(service, &socket_nodelay); |
michael@0 | 598 | PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 599 | |
michael@0 | 600 | rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &any_address); |
michael@0 | 601 | PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 602 | rv = PR_Bind(service, &any_address); PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 603 | rv = PR_Listen(service, 10); PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 604 | |
michael@0 | 605 | server_thread = PR_CreateThread( |
michael@0 | 606 | PR_USER_THREAD, Server, service, |
michael@0 | 607 | PR_PRIORITY_HIGH, thread_scope, |
michael@0 | 608 | PR_JOINABLE_THREAD, 16 * 1024); |
michael@0 | 609 | PR_ASSERT(NULL != server_thread); |
michael@0 | 610 | |
michael@0 | 611 | client_thread = PR_CreateThread( |
michael@0 | 612 | PR_USER_THREAD, Client, client, |
michael@0 | 613 | PR_PRIORITY_NORMAL, thread_scope, |
michael@0 | 614 | PR_JOINABLE_THREAD, 16 * 1024); |
michael@0 | 615 | PR_ASSERT(NULL != client_thread); |
michael@0 | 616 | |
michael@0 | 617 | rv = PR_JoinThread(client_thread); |
michael@0 | 618 | PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 619 | rv = PR_JoinThread(server_thread); |
michael@0 | 620 | PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 621 | |
michael@0 | 622 | rv = PR_Close(client); PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 623 | rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 624 | if (verbosity > silent) |
michael@0 | 625 | PR_fprintf(logFile, "Ending non-layered test\n"); |
michael@0 | 626 | |
michael@0 | 627 | /* with layering */ |
michael@0 | 628 | if (verbosity > silent) |
michael@0 | 629 | PR_fprintf(logFile, "Beginning layered test\n"); |
michael@0 | 630 | client = PR_NewTCPSocket(); PR_ASSERT(NULL != client); |
michael@0 | 631 | service = PR_NewTCPSocket(); PR_ASSERT(NULL != service); |
michael@0 | 632 | |
michael@0 | 633 | rv = PR_SetSocketOption(client, &socket_noblock); |
michael@0 | 634 | PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 635 | rv = PR_SetSocketOption(service, &socket_noblock); |
michael@0 | 636 | PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 637 | rv = PR_SetSocketOption(client, &socket_nodelay); |
michael@0 | 638 | PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 639 | rv = PR_SetSocketOption(service, &socket_nodelay); |
michael@0 | 640 | PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 641 | |
michael@0 | 642 | PushLayer(client); |
michael@0 | 643 | PushLayer(service); |
michael@0 | 644 | |
michael@0 | 645 | rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &any_address); |
michael@0 | 646 | PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 647 | rv = PR_Bind(service, &any_address); PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 648 | rv = PR_Listen(service, 10); PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 649 | |
michael@0 | 650 | server_thread = PR_CreateThread( |
michael@0 | 651 | PR_USER_THREAD, Server, service, |
michael@0 | 652 | PR_PRIORITY_HIGH, thread_scope, |
michael@0 | 653 | PR_JOINABLE_THREAD, 16 * 1024); |
michael@0 | 654 | PR_ASSERT(NULL != server_thread); |
michael@0 | 655 | |
michael@0 | 656 | client_thread = PR_CreateThread( |
michael@0 | 657 | PR_USER_THREAD, Client, client, |
michael@0 | 658 | PR_PRIORITY_NORMAL, thread_scope, |
michael@0 | 659 | PR_JOINABLE_THREAD, 16 * 1024); |
michael@0 | 660 | PR_ASSERT(NULL != client_thread); |
michael@0 | 661 | |
michael@0 | 662 | rv = PR_JoinThread(client_thread); |
michael@0 | 663 | PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 664 | rv = PR_JoinThread(server_thread); |
michael@0 | 665 | PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 666 | |
michael@0 | 667 | rv = PR_Close(PopLayer(client)); PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 668 | rv = PR_Close(PopLayer(service)); PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 669 | if (verbosity > silent) |
michael@0 | 670 | PR_fprintf(logFile, "Ending layered test\n"); |
michael@0 | 671 | } |
michael@0 | 672 | return 0; |
michael@0 | 673 | } /* main */ |
michael@0 | 674 | |
michael@0 | 675 | /* nblayer.c */ |