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 "prio.h" michael@0: #include "prprf.h" michael@0: #include "prlog.h" michael@0: #include "prnetdb.h" michael@0: #include "prthread.h" michael@0: michael@0: #include "plerror.h" michael@0: #include "plgetopt.h" michael@0: #include "prwin16.h" michael@0: michael@0: #include michael@0: #include michael@0: michael@0: /* michael@0: ** Testing layering of I/O michael@0: ** michael@0: ** The layered server michael@0: ** A thread that acts as a server. It creates a TCP listener with a dummy michael@0: ** layer pushed on top. Then listens for incoming connections. Each connection michael@0: ** request for connection will be layered as well, accept one request, echo michael@0: ** it back and close. michael@0: ** michael@0: ** The layered client michael@0: ** Pretty much what you'd expect. michael@0: */ michael@0: michael@0: static PRFileDesc *logFile; michael@0: static PRDescIdentity identity; michael@0: static PRNetAddr server_address; michael@0: michael@0: static PRIOMethods myMethods; michael@0: michael@0: typedef enum Verbosity {silent, quiet, chatty, noisy} Verbosity; michael@0: michael@0: static PRIntn minor_iterations = 5; michael@0: static PRIntn major_iterations = 1; michael@0: static Verbosity verbosity = quiet; michael@0: static PRUint16 default_port = 12273; michael@0: michael@0: static PRFileDesc *PushLayer(PRFileDesc *stack) michael@0: { michael@0: PRFileDesc *layer = PR_CreateIOLayerStub(identity, &myMethods); michael@0: PRStatus rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), layer); michael@0: if (verbosity > quiet) michael@0: PR_fprintf(logFile, "Pushed layer(0x%x) onto stack(0x%x)\n", layer, stack); michael@0: PR_ASSERT(PR_SUCCESS == rv); michael@0: return stack; michael@0: } /* PushLayer */ michael@0: michael@0: static PRFileDesc *PushNewLayers(PRFileDesc *stack) michael@0: { michael@0: PRDescIdentity tmp_identity; michael@0: PRFileDesc *layer; michael@0: PRStatus rv; michael@0: michael@0: /* push a dummy layer */ michael@0: tmp_identity = PR_GetUniqueIdentity("Dummy 1"); michael@0: layer = PR_CreateIOLayerStub(tmp_identity, PR_GetDefaultIOMethods()); michael@0: rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), layer); michael@0: if (verbosity > quiet) michael@0: PR_fprintf(logFile, "Pushed layer(0x%x) onto stack(0x%x)\n", layer, michael@0: stack); michael@0: PR_ASSERT(PR_SUCCESS == rv); michael@0: michael@0: /* push a data procesing layer */ michael@0: layer = PR_CreateIOLayerStub(identity, &myMethods); michael@0: rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), layer); michael@0: if (verbosity > quiet) michael@0: PR_fprintf(logFile, "Pushed layer(0x%x) onto stack(0x%x)\n", layer, michael@0: stack); michael@0: PR_ASSERT(PR_SUCCESS == rv); michael@0: michael@0: /* push another dummy layer */ michael@0: tmp_identity = PR_GetUniqueIdentity("Dummy 2"); michael@0: layer = PR_CreateIOLayerStub(tmp_identity, PR_GetDefaultIOMethods()); michael@0: rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), layer); michael@0: if (verbosity > quiet) michael@0: PR_fprintf(logFile, "Pushed layer(0x%x) onto stack(0x%x)\n", layer, michael@0: stack); michael@0: PR_ASSERT(PR_SUCCESS == rv); michael@0: return stack; michael@0: } /* PushLayer */ michael@0: michael@0: #if 0 michael@0: static PRFileDesc *PopLayer(PRFileDesc *stack) michael@0: { michael@0: PRFileDesc *popped = PR_PopIOLayer(stack, identity); michael@0: if (verbosity > quiet) michael@0: PR_fprintf(logFile, "Popped layer(0x%x) from stack(0x%x)\n", popped, stack); michael@0: popped->dtor(popped); michael@0: michael@0: return stack; michael@0: } /* PopLayer */ michael@0: #endif michael@0: michael@0: static void PR_CALLBACK Client(void *arg) michael@0: { michael@0: PRStatus rv; michael@0: PRUint8 buffer[100]; michael@0: PRIntn empty_flags = 0; michael@0: PRIntn bytes_read, bytes_sent; michael@0: PRFileDesc *stack = (PRFileDesc*)arg; michael@0: michael@0: /* Initialize the buffer so that Purify won't complain */ michael@0: memset(buffer, 0, sizeof(buffer)); michael@0: michael@0: rv = PR_Connect(stack, &server_address, PR_INTERVAL_NO_TIMEOUT); michael@0: PR_ASSERT(PR_SUCCESS == rv); michael@0: while (minor_iterations-- > 0) michael@0: { michael@0: bytes_sent = PR_Send( michael@0: stack, buffer, sizeof(buffer), empty_flags, PR_INTERVAL_NO_TIMEOUT); michael@0: PR_ASSERT(sizeof(buffer) == bytes_sent); michael@0: if (verbosity > chatty) michael@0: PR_fprintf(logFile, "Client sending %d bytes\n", bytes_sent); michael@0: bytes_read = PR_Recv( michael@0: stack, buffer, bytes_sent, empty_flags, PR_INTERVAL_NO_TIMEOUT); michael@0: if (verbosity > chatty) michael@0: PR_fprintf(logFile, "Client receiving %d bytes\n", bytes_read); michael@0: PR_ASSERT(bytes_read == bytes_sent); michael@0: } michael@0: michael@0: if (verbosity > quiet) michael@0: PR_fprintf(logFile, "Client shutting down stack\n"); michael@0: michael@0: rv = PR_Shutdown(stack, PR_SHUTDOWN_BOTH); PR_ASSERT(PR_SUCCESS == rv); michael@0: } /* Client */ michael@0: michael@0: static void PR_CALLBACK Server(void *arg) michael@0: { michael@0: PRStatus rv; michael@0: PRUint8 buffer[100]; michael@0: PRFileDesc *service; michael@0: PRUintn empty_flags = 0; michael@0: PRIntn bytes_read, bytes_sent; michael@0: PRFileDesc *stack = (PRFileDesc*)arg; michael@0: PRNetAddr client_address; michael@0: michael@0: service = PR_Accept(stack, &client_address, PR_INTERVAL_NO_TIMEOUT); michael@0: if (verbosity > quiet) michael@0: PR_fprintf(logFile, "Server accepting connection\n"); michael@0: michael@0: do michael@0: { michael@0: bytes_read = PR_Recv( michael@0: service, buffer, sizeof(buffer), empty_flags, PR_INTERVAL_NO_TIMEOUT); michael@0: if (0 != bytes_read) michael@0: { michael@0: if (verbosity > chatty) michael@0: PR_fprintf(logFile, "Server receiving %d bytes\n", bytes_read); michael@0: PR_ASSERT(bytes_read > 0); michael@0: bytes_sent = PR_Send( michael@0: service, buffer, bytes_read, empty_flags, PR_INTERVAL_NO_TIMEOUT); michael@0: if (verbosity > chatty) michael@0: PR_fprintf(logFile, "Server sending %d bytes\n", bytes_sent); michael@0: PR_ASSERT(bytes_read == bytes_sent); michael@0: } michael@0: michael@0: } while (0 != bytes_read); michael@0: michael@0: if (verbosity > quiet) michael@0: PR_fprintf(logFile, "Server shutting down and closing stack\n"); michael@0: rv = PR_Shutdown(service, PR_SHUTDOWN_BOTH); PR_ASSERT(PR_SUCCESS == rv); michael@0: rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv); michael@0: michael@0: } /* Server */ michael@0: michael@0: static PRInt32 PR_CALLBACK MyRecv( michael@0: PRFileDesc *fd, void *buf, PRInt32 amount, michael@0: PRIntn flags, PRIntervalTime timeout) michael@0: { michael@0: char *b = (char*)buf; michael@0: PRFileDesc *lo = fd->lower; michael@0: PRInt32 rv, readin = 0, request = 0; michael@0: rv = lo->methods->recv(lo, &request, sizeof(request), flags, timeout); michael@0: if (verbosity > chatty) PR_fprintf( michael@0: logFile, "MyRecv sending permission for %d bytes\n", request); michael@0: if (0 < rv) michael@0: { michael@0: if (verbosity > chatty) PR_fprintf( michael@0: logFile, "MyRecv received permission request for %d bytes\n", request); michael@0: rv = lo->methods->send( michael@0: lo, &request, sizeof(request), flags, timeout); michael@0: if (0 < rv) michael@0: { michael@0: if (verbosity > chatty) PR_fprintf( michael@0: logFile, "MyRecv sending permission for %d bytes\n", request); michael@0: while (readin < request) michael@0: { michael@0: rv = lo->methods->recv( michael@0: lo, b + readin, amount - readin, flags, timeout); michael@0: if (rv <= 0) break; michael@0: if (verbosity > chatty) PR_fprintf( michael@0: logFile, "MyRecv received %d bytes\n", rv); michael@0: readin += rv; michael@0: } michael@0: rv = readin; michael@0: } michael@0: } michael@0: return rv; michael@0: } /* MyRecv */ michael@0: michael@0: static PRInt32 PR_CALLBACK MySend( michael@0: PRFileDesc *fd, const void *buf, PRInt32 amount, michael@0: PRIntn flags, PRIntervalTime timeout) michael@0: { michael@0: PRFileDesc *lo = fd->lower; michael@0: const char *b = (const char*)buf; michael@0: PRInt32 rv, wroteout = 0, request; michael@0: if (verbosity > chatty) PR_fprintf( michael@0: logFile, "MySend asking permission to send %d bytes\n", amount); michael@0: rv = lo->methods->send(lo, &amount, sizeof(amount), flags, timeout); michael@0: if (0 < rv) michael@0: { michael@0: rv = lo->methods->recv( michael@0: lo, &request, sizeof(request), flags, timeout); michael@0: if (0 < rv) michael@0: { michael@0: PR_ASSERT(request == amount); michael@0: if (verbosity > chatty) PR_fprintf( michael@0: logFile, "MySend got permission to send %d bytes\n", request); michael@0: while (wroteout < request) michael@0: { michael@0: rv = lo->methods->send( michael@0: lo, b + wroteout, request - wroteout, flags, timeout); michael@0: if (rv <= 0) break; michael@0: if (verbosity > chatty) PR_fprintf( michael@0: logFile, "MySend wrote %d bytes\n", rv); michael@0: wroteout += rv; michael@0: } michael@0: rv = amount; michael@0: } michael@0: } michael@0: return rv; michael@0: } /* MySend */ michael@0: michael@0: static Verbosity ChangeVerbosity(Verbosity verbosity, PRIntn delta) michael@0: { michael@0: PRIntn verbage = (PRIntn)verbosity + delta; michael@0: if (verbage < (PRIntn)silent) verbage = (PRIntn)silent; michael@0: else if (verbage > (PRIntn)noisy) verbage = (PRIntn)noisy; michael@0: return (Verbosity)verbage; michael@0: } /* ChangeVerbosity */ michael@0: michael@0: int main(int argc, char **argv) michael@0: { michael@0: PRStatus rv; michael@0: PRIntn mits; michael@0: PLOptStatus os; michael@0: PRFileDesc *client, *service; michael@0: PRFileDesc *client_stack, *service_stack; michael@0: PRNetAddr any_address; michael@0: const char *server_name = NULL; michael@0: const PRIOMethods *stubMethods; michael@0: PRThread *client_thread, *server_thread; michael@0: PRThreadScope thread_scope = PR_LOCAL_THREAD; michael@0: PLOptState *opt = PL_CreateOptState(argc, argv, "dqGC:c:p:"); 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 0: michael@0: server_name = opt->value; michael@0: break; michael@0: case 'd': /* debug mode */ michael@0: if (verbosity < noisy) michael@0: verbosity = ChangeVerbosity(verbosity, 1); michael@0: break; michael@0: case 'q': /* debug mode */ michael@0: if (verbosity > silent) michael@0: verbosity = ChangeVerbosity(verbosity, -1); michael@0: break; michael@0: case 'G': /* use global threads */ michael@0: thread_scope = PR_GLOBAL_THREAD; michael@0: break; michael@0: case 'C': /* number of threads waiting */ michael@0: major_iterations = atoi(opt->value); michael@0: break; michael@0: case 'c': /* number of client threads */ michael@0: minor_iterations = atoi(opt->value); michael@0: break; michael@0: case 'p': /* default port */ michael@0: default_port = atoi(opt->value); michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: } michael@0: PL_DestroyOptState(opt); michael@0: PR_STDIO_INIT(); michael@0: michael@0: logFile = PR_GetSpecialFD(PR_StandardError); michael@0: michael@0: identity = PR_GetUniqueIdentity("Dummy"); michael@0: stubMethods = PR_GetDefaultIOMethods(); michael@0: michael@0: /* michael@0: ** The protocol we're going to implement is one where in order to initiate michael@0: ** a send, the sender must first solicit permission. Therefore, every michael@0: ** send is really a send - receive - send sequence. michael@0: */ michael@0: myMethods = *stubMethods; /* first get the entire batch */ michael@0: myMethods.recv = MyRecv; /* then override the ones we care about */ michael@0: myMethods.send = MySend; /* then override the ones we care about */ michael@0: michael@0: if (NULL == server_name) michael@0: rv = PR_InitializeNetAddr( michael@0: PR_IpAddrLoopback, default_port, &server_address); michael@0: else michael@0: { michael@0: rv = PR_StringToNetAddr(server_name, &server_address); michael@0: PR_ASSERT(PR_SUCCESS == rv); michael@0: rv = PR_InitializeNetAddr( michael@0: PR_IpAddrNull, default_port, &server_address); michael@0: } michael@0: PR_ASSERT(PR_SUCCESS == rv); michael@0: michael@0: /* one type w/o layering */ michael@0: michael@0: mits = minor_iterations; michael@0: while (major_iterations-- > 0) michael@0: { michael@0: if (verbosity > silent) michael@0: PR_fprintf(logFile, "Beginning non-layered test\n"); michael@0: client = PR_NewTCPSocket(); PR_ASSERT(NULL != client); michael@0: service = PR_NewTCPSocket(); PR_ASSERT(NULL != service); michael@0: rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &any_address); michael@0: PR_ASSERT(PR_SUCCESS == rv); michael@0: rv = PR_Bind(service, &any_address); PR_ASSERT(PR_SUCCESS == rv); michael@0: rv = PR_Listen(service, 10); PR_ASSERT(PR_SUCCESS == rv); michael@0: michael@0: minor_iterations = mits; michael@0: server_thread = PR_CreateThread( michael@0: PR_USER_THREAD, Server, service, michael@0: PR_PRIORITY_HIGH, thread_scope, michael@0: PR_JOINABLE_THREAD, 16 * 1024); michael@0: PR_ASSERT(NULL != server_thread); michael@0: michael@0: client_thread = PR_CreateThread( michael@0: PR_USER_THREAD, Client, client, michael@0: PR_PRIORITY_NORMAL, thread_scope, michael@0: PR_JOINABLE_THREAD, 16 * 1024); michael@0: PR_ASSERT(NULL != client_thread); michael@0: michael@0: rv = PR_JoinThread(client_thread); michael@0: PR_ASSERT(PR_SUCCESS == rv); michael@0: rv = PR_JoinThread(server_thread); michael@0: PR_ASSERT(PR_SUCCESS == rv); michael@0: michael@0: rv = PR_Close(client); PR_ASSERT(PR_SUCCESS == rv); michael@0: rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv); michael@0: if (verbosity > silent) michael@0: PR_fprintf(logFile, "Ending non-layered test\n"); michael@0: michael@0: /* with layering */ michael@0: if (verbosity > silent) michael@0: PR_fprintf(logFile, "Beginning layered test\n"); michael@0: client = PR_NewTCPSocket(); PR_ASSERT(NULL != client); michael@0: PushLayer(client); michael@0: service = PR_NewTCPSocket(); PR_ASSERT(NULL != service); michael@0: PushLayer(service); michael@0: rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &any_address); michael@0: PR_ASSERT(PR_SUCCESS == rv); michael@0: rv = PR_Bind(service, &any_address); PR_ASSERT(PR_SUCCESS == rv); michael@0: rv = PR_Listen(service, 10); PR_ASSERT(PR_SUCCESS == rv); michael@0: michael@0: minor_iterations = mits; michael@0: server_thread = PR_CreateThread( michael@0: PR_USER_THREAD, Server, service, michael@0: PR_PRIORITY_HIGH, thread_scope, michael@0: PR_JOINABLE_THREAD, 16 * 1024); michael@0: PR_ASSERT(NULL != server_thread); michael@0: michael@0: client_thread = PR_CreateThread( michael@0: PR_USER_THREAD, Client, client, michael@0: PR_PRIORITY_NORMAL, thread_scope, michael@0: PR_JOINABLE_THREAD, 16 * 1024); michael@0: PR_ASSERT(NULL != client_thread); michael@0: michael@0: rv = PR_JoinThread(client_thread); michael@0: PR_ASSERT(PR_SUCCESS == rv); michael@0: rv = PR_JoinThread(server_thread); michael@0: PR_ASSERT(PR_SUCCESS == rv); michael@0: michael@0: rv = PR_Close(client); PR_ASSERT(PR_SUCCESS == rv); michael@0: rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv); michael@0: /* with layering, using new style stack */ michael@0: if (verbosity > silent) michael@0: PR_fprintf(logFile, michael@0: "Beginning layered test with new style stack\n"); michael@0: client = PR_NewTCPSocket(); PR_ASSERT(NULL != client); michael@0: client_stack = PR_CreateIOLayer(client); michael@0: PushNewLayers(client_stack); michael@0: service = PR_NewTCPSocket(); PR_ASSERT(NULL != service); michael@0: service_stack = PR_CreateIOLayer(service); michael@0: PushNewLayers(service_stack); michael@0: rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &any_address); michael@0: PR_ASSERT(PR_SUCCESS == rv); michael@0: rv = PR_Bind(service, &any_address); PR_ASSERT(PR_SUCCESS == rv); michael@0: rv = PR_Listen(service, 10); PR_ASSERT(PR_SUCCESS == rv); michael@0: michael@0: minor_iterations = mits; michael@0: server_thread = PR_CreateThread( michael@0: PR_USER_THREAD, Server, service_stack, michael@0: PR_PRIORITY_HIGH, thread_scope, michael@0: PR_JOINABLE_THREAD, 16 * 1024); michael@0: PR_ASSERT(NULL != server_thread); michael@0: michael@0: client_thread = PR_CreateThread( michael@0: PR_USER_THREAD, Client, client_stack, michael@0: PR_PRIORITY_NORMAL, thread_scope, michael@0: PR_JOINABLE_THREAD, 16 * 1024); michael@0: PR_ASSERT(NULL != client_thread); michael@0: michael@0: rv = PR_JoinThread(client_thread); michael@0: PR_ASSERT(PR_SUCCESS == rv); michael@0: rv = PR_JoinThread(server_thread); michael@0: PR_ASSERT(PR_SUCCESS == rv); michael@0: michael@0: rv = PR_Close(client_stack); PR_ASSERT(PR_SUCCESS == rv); michael@0: rv = PR_Close(service_stack); PR_ASSERT(PR_SUCCESS == rv); michael@0: if (verbosity > silent) michael@0: PR_fprintf(logFile, "Ending layered test\n"); michael@0: } michael@0: return 0; michael@0: } /* main */ michael@0: michael@0: /* layer.c */