michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nspr.h" michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include "plerror.h" michael@0: #include "plgetopt.h" michael@0: michael@0: #define BASE_PORT 9867 michael@0: #define DEFAULT_THREADS 1 michael@0: #define DEFAULT_BACKLOG 10 michael@0: #define DEFAULT_TIMEOUT 10 michael@0: #define RANDOM_RANGE 100 /* should be significantly smaller than RAND_MAX */ michael@0: michael@0: typedef enum {running, stopped} Status; michael@0: michael@0: typedef struct Shared michael@0: { michael@0: PRLock *ml; michael@0: PRCondVar *cv; michael@0: PRBool passed; michael@0: PRBool random; michael@0: PRFileDesc *debug; michael@0: PRIntervalTime timeout; michael@0: PRFileDesc *listenSock; michael@0: Status status; michael@0: } Shared; michael@0: michael@0: static PRIntervalTime Timeout(const Shared *shared) michael@0: { michael@0: PRIntervalTime timeout = shared->timeout; michael@0: if (shared->random) michael@0: { michael@0: PRIntervalTime half = timeout >> 1; /* one half of the interval */ michael@0: PRIntervalTime quarter = half >> 1; /* one quarter of the interval */ michael@0: /* something in [0..timeout / 2) */ michael@0: PRUint32 random = (rand() % RANDOM_RANGE) * half / RANDOM_RANGE; michael@0: timeout = (3 * quarter) + random; /* [75..125)% */ michael@0: } michael@0: return timeout; michael@0: } /* Timeout */ michael@0: michael@0: static void Accept(void *arg) michael@0: { michael@0: PRStatus rv; michael@0: char *buffer = NULL; michael@0: PRNetAddr clientAddr; michael@0: Shared *shared = (Shared*)arg; michael@0: PRInt32 recv_length = 0, flags = 0; michael@0: PRFileDesc *clientSock; michael@0: PRIntn toread, byte, bytes, loop = 0; michael@0: struct Descriptor { PRInt32 length; PRUint32 checksum; } descriptor; michael@0: michael@0: do michael@0: { michael@0: PRUint32 checksum = 0; michael@0: if (NULL != shared->debug) michael@0: PR_fprintf(shared->debug, "[%d]accepting ... ", loop++); michael@0: clientSock = PR_Accept( michael@0: shared->listenSock, &clientAddr, Timeout(shared)); michael@0: if (clientSock != NULL) michael@0: { michael@0: if (NULL != shared->debug) michael@0: PR_fprintf(shared->debug, "reading length ... "); michael@0: bytes = PR_Recv( michael@0: clientSock, &descriptor, sizeof(descriptor), michael@0: flags, Timeout(shared)); michael@0: if (sizeof(descriptor) == bytes) michael@0: { michael@0: /* and, before doing something stupid ... */ michael@0: descriptor.length = PR_ntohl(descriptor.length); michael@0: descriptor.checksum = PR_ntohl(descriptor.checksum); michael@0: if (NULL != shared->debug) michael@0: PR_fprintf(shared->debug, "%d bytes ... ", descriptor.length); michael@0: toread = descriptor.length; michael@0: if (recv_length < descriptor.length) michael@0: { michael@0: if (NULL != buffer) PR_DELETE(buffer); michael@0: buffer = (char*)PR_MALLOC(descriptor.length); michael@0: recv_length = descriptor.length; michael@0: } michael@0: for (toread = descriptor.length; toread > 0; toread -= bytes) michael@0: { michael@0: bytes = PR_Recv( michael@0: clientSock, &buffer[descriptor.length - toread], michael@0: toread, flags, Timeout(shared)); michael@0: if (-1 == bytes) michael@0: { michael@0: if (NULL != shared->debug) michael@0: PR_fprintf(shared->debug, "read data failed..."); michael@0: bytes = 0; michael@0: } michael@0: } michael@0: } michael@0: else if (NULL != shared->debug) michael@0: { michael@0: PR_fprintf(shared->debug, "read desciptor failed..."); michael@0: descriptor.length = -1; michael@0: } michael@0: if (NULL != shared->debug) michael@0: PR_fprintf(shared->debug, "closing"); michael@0: rv = PR_Shutdown(clientSock, PR_SHUTDOWN_BOTH); michael@0: if ((PR_FAILURE == rv) && (NULL != shared->debug)) michael@0: { michael@0: PR_fprintf(shared->debug, " failed"); michael@0: shared->passed = PR_FALSE; michael@0: } michael@0: rv = PR_Close(clientSock); michael@0: if (PR_FAILURE == rv) if (NULL != shared->debug) michael@0: { michael@0: PR_fprintf(shared->debug, " failed"); michael@0: shared->passed = PR_FALSE; michael@0: } michael@0: if (descriptor.length > 0) michael@0: { michael@0: for (byte = 0; byte < descriptor.length; ++byte) michael@0: { michael@0: PRUint32 overflow = checksum & 0x80000000; michael@0: checksum = (checksum << 1); michael@0: if (0x00000000 != overflow) checksum += 1; michael@0: checksum += buffer[byte]; michael@0: } michael@0: if ((descriptor.checksum != checksum) && (NULL != shared->debug)) michael@0: { michael@0: PR_fprintf(shared->debug, " ... data mismatch"); michael@0: shared->passed = PR_FALSE; michael@0: } michael@0: } michael@0: else if (0 == descriptor.length) michael@0: { michael@0: PR_Lock(shared->ml); michael@0: shared->status = stopped; michael@0: PR_NotifyCondVar(shared->cv); michael@0: PR_Unlock(shared->ml); michael@0: } michael@0: if (NULL != shared->debug) michael@0: PR_fprintf(shared->debug, "\n"); michael@0: } michael@0: else michael@0: { michael@0: if (PR_PENDING_INTERRUPT_ERROR != PR_GetError()) michael@0: { michael@0: if (NULL != shared->debug) PL_PrintError("Accept"); michael@0: shared->passed = PR_FALSE; michael@0: } michael@0: } michael@0: } while (running == shared->status); michael@0: if (NULL != buffer) PR_DELETE(buffer); michael@0: } /* Accept */ michael@0: michael@0: PRIntn Tmoacc(PRIntn argc, char **argv) michael@0: { michael@0: PRStatus rv; michael@0: PRIntn exitStatus; michael@0: PRIntn index; michael@0: Shared *shared; michael@0: PLOptStatus os; michael@0: PRThread **thread; michael@0: PRNetAddr listenAddr; michael@0: PRSocketOptionData sockOpt; michael@0: PRIntn timeout = DEFAULT_TIMEOUT; michael@0: PRIntn threads = DEFAULT_THREADS; michael@0: PRIntn backlog = DEFAULT_BACKLOG; michael@0: PRThreadScope thread_scope = PR_LOCAL_THREAD; michael@0: michael@0: PLOptState *opt = PL_CreateOptState(argc, argv, "dGb:t:T:R"); michael@0: michael@0: shared = PR_NEWZAP(Shared); michael@0: michael@0: shared->debug = NULL; michael@0: shared->passed = PR_TRUE; michael@0: shared->random = PR_TRUE; michael@0: shared->status = running; michael@0: shared->ml = PR_NewLock(); michael@0: shared->cv = PR_NewCondVar(shared->ml); michael@0: michael@0: while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) michael@0: { michael@0: if (PL_OPT_BAD == os) continue; michael@0: switch (opt->option) michael@0: { michael@0: case 'd': /* debug mode */ michael@0: shared->debug = PR_GetSpecialFD(PR_StandardError); michael@0: break; michael@0: case 'G': /* use global threads */ michael@0: thread_scope = PR_GLOBAL_THREAD; michael@0: break; michael@0: case 'b': /* size of listen backlog */ michael@0: backlog = atoi(opt->value); michael@0: break; michael@0: case 't': /* number of threads doing accept */ michael@0: threads = atoi(opt->value); michael@0: break; michael@0: case 'T': /* timeout used for network operations */ michael@0: timeout = atoi(opt->value); michael@0: break; michael@0: case 'R': /* randomize the timeout values */ michael@0: shared->random = PR_TRUE; michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: } michael@0: PL_DestroyOptState(opt); michael@0: if (0 == threads) threads = DEFAULT_THREADS; michael@0: if (0 == backlog) backlog = DEFAULT_BACKLOG; michael@0: if (0 == timeout) timeout = DEFAULT_TIMEOUT; michael@0: michael@0: PR_STDIO_INIT(); michael@0: memset(&listenAddr, 0, sizeof(listenAddr)); michael@0: rv = PR_InitializeNetAddr(PR_IpAddrAny, BASE_PORT, &listenAddr); michael@0: PR_ASSERT(PR_SUCCESS == rv); michael@0: michael@0: shared->timeout = PR_SecondsToInterval(timeout); michael@0: michael@0: /* First bind to the socket */ michael@0: shared->listenSock = PR_NewTCPSocket(); michael@0: if (shared->listenSock) michael@0: { michael@0: sockOpt.option = PR_SockOpt_Reuseaddr; michael@0: sockOpt.value.reuse_addr = PR_TRUE; michael@0: rv = PR_SetSocketOption(shared->listenSock, &sockOpt); michael@0: PR_ASSERT(PR_SUCCESS == rv); michael@0: rv = PR_Bind(shared->listenSock, &listenAddr); michael@0: if (rv != PR_FAILURE) michael@0: { michael@0: rv = PR_Listen(shared->listenSock, threads + backlog); michael@0: if (PR_SUCCESS == rv) michael@0: { michael@0: thread = (PRThread**)PR_CALLOC(threads * sizeof(PRThread*)); michael@0: for (index = 0; index < threads; ++index) michael@0: { michael@0: thread[index] = PR_CreateThread( michael@0: PR_USER_THREAD, Accept, shared, michael@0: PR_PRIORITY_NORMAL, thread_scope, michael@0: PR_JOINABLE_THREAD, 0); michael@0: PR_ASSERT(NULL != thread[index]); michael@0: } michael@0: michael@0: PR_Lock(shared->ml); michael@0: while (shared->status == running) michael@0: PR_WaitCondVar(shared->cv, PR_INTERVAL_NO_TIMEOUT); michael@0: PR_Unlock(shared->ml); michael@0: for (index = 0; index < threads; ++index) michael@0: { michael@0: rv = PR_Interrupt(thread[index]); michael@0: PR_ASSERT(PR_SUCCESS== rv); michael@0: rv = PR_JoinThread(thread[index]); michael@0: PR_ASSERT(PR_SUCCESS== rv); michael@0: } michael@0: PR_DELETE(thread); michael@0: } michael@0: else michael@0: { michael@0: if (shared->debug) PL_PrintError("Listen"); michael@0: shared->passed = PR_FALSE; michael@0: } michael@0: } michael@0: else michael@0: { michael@0: if (shared->debug) PL_PrintError("Bind"); michael@0: shared->passed = PR_FALSE; michael@0: } michael@0: michael@0: PR_Close(shared->listenSock); michael@0: } michael@0: else michael@0: { michael@0: if (shared->debug) PL_PrintError("Create"); michael@0: shared->passed = PR_FALSE; michael@0: } michael@0: michael@0: PR_DestroyCondVar(shared->cv); michael@0: PR_DestroyLock(shared->ml); michael@0: michael@0: PR_fprintf( michael@0: PR_GetSpecialFD(PR_StandardError), "%s\n", michael@0: ((shared->passed) ? "PASSED" : "FAILED")); michael@0: michael@0: exitStatus = (shared->passed) ? 0 : 1; michael@0: PR_DELETE(shared); michael@0: return exitStatus; michael@0: } michael@0: michael@0: int main(int argc, char **argv) michael@0: { michael@0: return (PR_VersionCheck(PR_VERSION)) ? michael@0: PR_Initialize(Tmoacc, argc, argv, 4) : -1; michael@0: } /* main */ michael@0: michael@0: /* tmoacc */