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: /*********************************************************************** michael@0: ** michael@0: ** Name: tmocon.c michael@0: ** michael@0: ** Description: test client socket connection. michael@0: ** michael@0: ** Modification History: michael@0: ** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. michael@0: ** The debug mode will print all of the printfs associated with this test. michael@0: ** The regress mode will be the default mode. Since the regress tool limits michael@0: ** the output to a one line status:PASS or FAIL,all of the printf statements michael@0: ** have been handled with an if (debug_mode) statement. michael@0: ***********************************************************************/ michael@0: michael@0: /*********************************************************************** michael@0: ** Includes michael@0: ***********************************************************************/ michael@0: /* Used to get the command line option */ michael@0: #include "plgetopt.h" michael@0: michael@0: #include "nspr.h" michael@0: #include "pprio.h" michael@0: michael@0: #include "plerror.h" michael@0: #include "plgetopt.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: /* for getcwd */ michael@0: #if defined(XP_UNIX) || defined (XP_OS2) || defined(XP_BEOS) michael@0: #include michael@0: #elif defined(XP_PC) michael@0: #include michael@0: #endif michael@0: michael@0: #ifdef WINCE michael@0: #include michael@0: char *getcwd(char *buf, size_t size) michael@0: { michael@0: wchar_t wpath[MAX_PATH]; michael@0: _wgetcwd(wpath, MAX_PATH); michael@0: WideCharToMultiByte(CP_ACP, 0, wpath, -1, buf, size, 0, 0); michael@0: } michael@0: #endif michael@0: michael@0: #define BASE_PORT 9867 michael@0: michael@0: #define DEFAULT_DALLY 1 michael@0: #define DEFAULT_THREADS 1 michael@0: #define DEFAULT_TIMEOUT 10 michael@0: #define DEFAULT_MESSAGES 100 michael@0: #define DEFAULT_MESSAGESIZE 100 michael@0: michael@0: static PRFileDesc *debug_out = NULL; michael@0: michael@0: typedef struct Shared michael@0: { michael@0: PRBool random; michael@0: PRBool failed; michael@0: PRBool intermittant; michael@0: PRIntn debug; michael@0: PRInt32 messages; michael@0: PRIntervalTime dally; michael@0: PRIntervalTime timeout; michael@0: PRInt32 message_length; michael@0: PRNetAddr serverAddress; 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 quarter = timeout >> 2; /* one quarter of the interval */ michael@0: PRUint32 random = rand() % quarter; /* something in[0..timeout / 4) */ michael@0: timeout = (((3 * quarter) + random) >> 2) + quarter; /* [75..125)% */ michael@0: } michael@0: return timeout; michael@0: } /* Timeout */ michael@0: michael@0: static void CauseTimeout(const Shared *shared) michael@0: { michael@0: if (shared->intermittant) PR_Sleep(Timeout(shared)); michael@0: } /* CauseTimeout */ michael@0: michael@0: static PRStatus MakeReceiver(Shared *shared) michael@0: { michael@0: PRStatus rv = PR_FAILURE; michael@0: if (PR_IsNetAddrType(&shared->serverAddress, PR_IpAddrLoopback)) michael@0: { michael@0: char *argv[3]; michael@0: char path[1024 + sizeof("/tmoacc")]; michael@0: michael@0: getcwd(path, sizeof(path)); michael@0: michael@0: (void)strcat(path, "/tmoacc"); michael@0: #ifdef XP_PC michael@0: (void)strcat(path, ".exe"); michael@0: #endif michael@0: argv[0] = path; michael@0: if (shared->debug > 0) michael@0: { michael@0: argv[1] = "-d"; michael@0: argv[2] = NULL; michael@0: } michael@0: else argv[1] = NULL; michael@0: if (shared->debug > 1) michael@0: PR_fprintf(debug_out, " creating accept process %s ...", path); michael@0: fflush(stdout); michael@0: rv = PR_CreateProcessDetached(path, argv, NULL, NULL); michael@0: if (PR_SUCCESS == rv) michael@0: { michael@0: if (shared->debug > 1) michael@0: PR_fprintf(debug_out, " wait 5 seconds"); michael@0: if (shared->debug > 1) michael@0: PR_fprintf(debug_out, " before connecting to accept process ..."); michael@0: fflush(stdout); michael@0: PR_Sleep(PR_SecondsToInterval(5)); michael@0: return rv; michael@0: } michael@0: shared->failed = PR_TRUE; michael@0: if (shared->debug > 0) michael@0: PL_FPrintError(debug_out, "PR_CreateProcessDetached failed"); michael@0: } michael@0: return rv; michael@0: } /* MakeReceiver */ michael@0: michael@0: static void Connect(void *arg) michael@0: { michael@0: PRStatus rv; michael@0: char *buffer = NULL; michael@0: PRFileDesc *clientSock; michael@0: Shared *shared = (Shared*)arg; michael@0: PRInt32 loop, bytes, flags = 0; michael@0: struct Descriptor { PRInt32 length; PRUint32 checksum; } descriptor; michael@0: debug_out = (0 == shared->debug) ? NULL : PR_GetSpecialFD(PR_StandardError); michael@0: michael@0: buffer = (char*)PR_MALLOC(shared->message_length); michael@0: michael@0: for (bytes = 0; bytes < shared->message_length; ++bytes) michael@0: buffer[bytes] = (char)bytes; michael@0: michael@0: descriptor.checksum = 0; michael@0: for (bytes = 0; bytes < shared->message_length; ++bytes) michael@0: { michael@0: PRUint32 overflow = descriptor.checksum & 0x80000000; michael@0: descriptor.checksum = (descriptor.checksum << 1); michael@0: if (0x00000000 != overflow) descriptor.checksum += 1; michael@0: descriptor.checksum += buffer[bytes]; michael@0: } michael@0: descriptor.checksum = PR_htonl(descriptor.checksum); michael@0: michael@0: for (loop = 0; loop < shared->messages; ++loop) michael@0: { michael@0: if (shared->debug > 1) michael@0: PR_fprintf(debug_out, "[%d]socket ... ", loop); michael@0: clientSock = PR_NewTCPSocket(); michael@0: if (clientSock) michael@0: { michael@0: /* michael@0: * We need to slow down the rate of generating connect requests, michael@0: * otherwise the listen backlog queue on the accept side may michael@0: * become full and we will get connection refused or timeout michael@0: * error. michael@0: */ michael@0: michael@0: PR_Sleep(shared->dally); michael@0: if (shared->debug > 1) michael@0: { michael@0: char buf[128]; michael@0: PR_NetAddrToString(&shared->serverAddress, buf, sizeof(buf)); michael@0: PR_fprintf(debug_out, "connecting to %s ... ", buf); michael@0: } michael@0: rv = PR_Connect( michael@0: clientSock, &shared->serverAddress, Timeout(shared)); michael@0: if (PR_SUCCESS == rv) michael@0: { michael@0: PRInt32 descriptor_length = (loop < (shared->messages - 1)) ? michael@0: shared->message_length : 0; michael@0: descriptor.length = PR_htonl(descriptor_length); michael@0: if (shared->debug > 1) michael@0: PR_fprintf( michael@0: debug_out, "sending %d bytes ... ", descriptor_length); michael@0: CauseTimeout(shared); /* might cause server to timeout */ michael@0: bytes = PR_Send( michael@0: clientSock, &descriptor, sizeof(descriptor), michael@0: flags, Timeout(shared)); michael@0: if (bytes != sizeof(descriptor)) michael@0: { michael@0: shared->failed = PR_TRUE; michael@0: if (shared->debug > 0) michael@0: PL_FPrintError(debug_out, "PR_Send failed"); michael@0: } michael@0: if (0 != descriptor_length) michael@0: { michael@0: CauseTimeout(shared); michael@0: bytes = PR_Send( michael@0: clientSock, buffer, descriptor_length, michael@0: flags, Timeout(shared)); michael@0: if (bytes != descriptor_length) michael@0: { michael@0: shared->failed = PR_TRUE; michael@0: if (shared->debug > 0) michael@0: PL_FPrintError(debug_out, "PR_Send failed"); michael@0: } michael@0: } michael@0: if (shared->debug > 1) PR_fprintf(debug_out, "closing ... "); michael@0: rv = PR_Shutdown(clientSock, PR_SHUTDOWN_BOTH); michael@0: rv = PR_Close(clientSock); michael@0: if (shared->debug > 1) michael@0: { michael@0: if (PR_SUCCESS == rv) PR_fprintf(debug_out, "\n"); michael@0: else PL_FPrintError(debug_out, "shutdown failed"); michael@0: } michael@0: } michael@0: else michael@0: { michael@0: if (shared->debug > 1) PL_FPrintError(debug_out, "connect failed"); michael@0: PR_Close(clientSock); michael@0: if ((loop == 0) && (PR_GetError() == PR_CONNECT_REFUSED_ERROR)) michael@0: { michael@0: if (MakeReceiver(shared) == PR_FAILURE) break; michael@0: } michael@0: else michael@0: { michael@0: if (shared->debug > 1) PR_fprintf(debug_out, " exiting\n"); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: else michael@0: { michael@0: shared->failed = PR_TRUE; michael@0: if (shared->debug > 0) PL_FPrintError(debug_out, "create socket"); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: PR_DELETE(buffer); michael@0: } /* Connect */ michael@0: michael@0: int Tmocon(int argc, char **argv) michael@0: { michael@0: /* michael@0: * USAGE michael@0: * -d turn on debugging output (default = off) michael@0: * -v turn on verbose output (default = off) michael@0: * -h dns name of host serving the connection (default = self) michael@0: * -i dally intermittantly to cause timeouts (default = off) michael@0: * -m number of messages to send (default = 100) michael@0: * -s size of each message (default = 100) michael@0: * -t number of threads sending (default = 1) michael@0: * -G use global threads (default = local) michael@0: * -T timeout on I/O operations (seconds) (default = 10) michael@0: * -D dally between connect requests (seconds)(default = 0) michael@0: * -R randomize the dally types around 'T' (default = no) michael@0: */ michael@0: michael@0: PRStatus rv; michael@0: int exitStatus; michael@0: PLOptStatus os; michael@0: Shared *shared = NULL; michael@0: PRThread **thread = NULL; michael@0: PRIntn index, threads = DEFAULT_THREADS; michael@0: PRThreadScope thread_scope = PR_LOCAL_THREAD; michael@0: PRInt32 dally = DEFAULT_DALLY, timeout = DEFAULT_TIMEOUT; michael@0: PLOptState *opt = PL_CreateOptState(argc, argv, "divGRh:m:s:t:T:D:"); michael@0: michael@0: shared = PR_NEWZAP(Shared); michael@0: michael@0: shared->debug = 0; michael@0: shared->failed = PR_FALSE; michael@0: shared->random = PR_FALSE; michael@0: shared->messages = DEFAULT_MESSAGES; michael@0: shared->message_length = DEFAULT_MESSAGESIZE; michael@0: michael@0: PR_STDIO_INIT(); michael@0: memset(&shared->serverAddress, 0, sizeof(shared->serverAddress)); michael@0: rv = PR_InitializeNetAddr(PR_IpAddrLoopback, BASE_PORT, &shared->serverAddress); michael@0: PR_ASSERT(PR_SUCCESS == rv); 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': michael@0: if (0 == shared->debug) shared->debug = 1; michael@0: break; michael@0: case 'v': michael@0: if (0 == shared->debug) shared->debug = 2; michael@0: break; michael@0: case 'i': michael@0: shared->intermittant = PR_TRUE; michael@0: break; michael@0: case 'R': michael@0: shared->random = PR_TRUE; michael@0: break; michael@0: case 'G': michael@0: thread_scope = PR_GLOBAL_THREAD; michael@0: break; michael@0: case 'h': /* the value for backlock */ michael@0: { michael@0: PRIntn es = 0; michael@0: PRHostEnt host; michael@0: char buffer[1024]; michael@0: (void)PR_GetHostByName( michael@0: opt->value, buffer, sizeof(buffer), &host); michael@0: es = PR_EnumerateHostEnt( michael@0: es, &host, BASE_PORT, &shared->serverAddress); michael@0: PR_ASSERT(es > 0); michael@0: } michael@0: break; michael@0: case 'm': /* number of messages to send */ michael@0: shared->messages = atoi(opt->value); michael@0: break; michael@0: case 't': /* number of threads sending */ michael@0: threads = atoi(opt->value); michael@0: break; michael@0: case 'D': /* dally time between transmissions */ michael@0: dally = atoi(opt->value); michael@0: break; michael@0: case 'T': /* timeout on I/O operations */ michael@0: timeout = atoi(opt->value); michael@0: break; michael@0: case 's': /* total size of each message */ michael@0: shared->message_length = atoi(opt->value); michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: } michael@0: PL_DestroyOptState(opt); michael@0: michael@0: if (0 == timeout) timeout = DEFAULT_TIMEOUT; michael@0: if (0 == threads) threads = DEFAULT_THREADS; michael@0: if (0 == shared->messages) shared->messages = DEFAULT_MESSAGES; michael@0: if (0 == shared->message_length) shared->message_length = DEFAULT_MESSAGESIZE; michael@0: michael@0: shared->dally = PR_SecondsToInterval(dally); michael@0: shared->timeout = PR_SecondsToInterval(timeout); michael@0: michael@0: thread = (PRThread**)PR_CALLOC(threads * sizeof(PRThread*)); michael@0: michael@0: for (index = 0; index < threads; ++index) michael@0: thread[index] = PR_CreateThread( michael@0: PR_USER_THREAD, Connect, shared, michael@0: PR_PRIORITY_NORMAL, thread_scope, michael@0: PR_JOINABLE_THREAD, 0); michael@0: for (index = 0; index < threads; ++index) michael@0: rv = PR_JoinThread(thread[index]); michael@0: michael@0: PR_DELETE(thread); michael@0: michael@0: PR_fprintf( michael@0: PR_GetSpecialFD(PR_StandardError), "%s\n", michael@0: ((shared->failed) ? "FAILED" : "PASSED")); michael@0: exitStatus = (shared->failed) ? 1 : 0; 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(Tmocon, argc, argv, 4) : -1; michael@0: } /* main */ michael@0: michael@0: /* tmocon.c */ michael@0: michael@0: