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