/*++

Copyright (C) Microsoft Corporation, 1994 - 1999

Module Name:

    Server.c

Abstract:

    Server side of RPC runtime performance test.

Author:

    Mario Goertzel (mariogo)   31-Mar-1994

Revision History:

--*/

#include <rpcperf.h>
#include <assert.h>

#include <DataTran.h>
#include <DTCommon.h>

// Usage
const char *USAGE =
           "[-i iterations] [-l logfile] [-n Clients] [-n MinThreads]\n"
           "\t\t[-c ChunkSize] [-z size] [-d FtpRoot] [-w WwwRoot] [-n case] *\n"
           "      Clients    - default 1\n"
           "      MinThreads - default 3\n"
           "      FtpRoot    - root directory for ftp\n"
           "                   required for FTP tests\n"
           "      WwwRoot    - root diretory for Http\n"
           "                   required for HTTP tests\n"
           "      Size       - default 50k,100k,200k,400k\n"
           "      ChunkSize  - default 4000,8192,32768\n"
           "      filename   - you may specify a file to transfer (not implemented yet)\n\n"
           "      Cases, you may specify up to five, default is all:\n"
           "        0: Write using RPC\n"
           "        1: Read  using RPC\n"
           "        2: Write using RPC Pipes\n"
           "        3: Read  using RPC Pipes\n"
           "        4: Write using FTP (InternetReadFile)\n"
           "        5: Read  using FTP (InternetWriteFile)\n"
           "        6: Write using HTTP Get\n"
           "        7: Read  using HTTP Put\n"
           "        8: Write using RPC with File I/O\n"
           "        9: Read  using RPC with File I/O\n"
           "       10: Write using RPC Pipes with File I/O\n"
           "       11: Read  using RPC Pipes with File I/O\n"
           "       12: Write using FTP\n"
           "       13: Read  using FTP\n"
           "       14: Write using FTP (InternetReadFile)  with File I/O\n"
           "       15: Read  using FTP (InternetWriteFile) with File I/O\n"
           "       16: Write using HTTP Get with File I/O\n"
           "       17: Read  using HTTP Put\n"
           "\n"
           "        Note: Results are milliseconds/call/client\n"
           ;


//
// Globals making it easier.
//

int  CurrentChunkIndex;
int  CurrentTotalIndex;
int  CurrentCase;
char TestCases[TEST_MAX];


char *pFtpRoot = NULL;      //  Root Directory used for FTP
char *pWwwRoot = NULL;      //  Root Directory used for HTTP

//
//  If this is non-NULL, then use this file instead of creating
//  temporary ones.  Not implemented...
//
char *pFileName = 0;

long            CurrentIter;
unsigned long  *Results;

long Clients, ActiveClients, ClientsLeft;

CRITICAL_SECTION CritSec;
HANDLE GoEvent   = 0;
HANDLE DoneEvent = 0;

char *TestNames[TEST_MAX] =
    {
    "S to C RPC          : %9ld / %7ld",
    "C to S RPC          : %9ld / %7ld",
    "S to C Pipes        : %9ld / %7ld",
    "C to S Pipes        : %9ld / %7ld",
    "S to C FTP1         : %9ld / %7ld",
    "C to S FTP1         : %9ld / %7ld",
    "S to C HTTP         : %9ld / %7ld",
    "C to S HTTP         : %9ld / %7ld",
    "S to C RPC   w/File : %9ld / %7ld",
    "C to S RPC   w/File : %9ld / %7ld",
    "S to C Pipes w/File : %9ld / %7ld",
    "C to S Pipes w/File : %9ld / %7ld",
    "S to C FTP   w/File : %9ld / %7ld",
    "C to S FTP   w/File : %9ld / %7ld",
    "S to C FTP1  w/File : %9ld / %7ld",
    "C to S FTP1  w/File : %9ld / %7ld",
    "S to C HTTP  w/File : %9ld / %7ld",
    "C to S HTTP  w/File : %9ld / %7ld"
    };

//
//  These arrays are NULL terminated.  Don't let the last entry get used!
//
unsigned long ChunkSizes[10] = {4000, 8192, 32*1024L, 0, 0, 0, 0, 0, 0, 0};
unsigned long TotalSizes[10] = {50L*1024L, 100L*1024L, 200L*1024L, 400L*1024L, 0,
                                0, 0, 0, 0, 0};


///////////////////////////////////////////////////////////////////////////////
/*++
    FUNCTION:    DTParseArgv
    DESCRIPTION: Parses arguments
--*/
static void DTParseArgv(int argc, char **argv)
{
    int fMissingParm = 0;
    char *Name = *argv;
    char option;
    int  options_count;

    int  totalsize_count = 0;
    int  chunksize_count = 0;

    for(options_count = 0; options_count < 7; options_count++)
        Options[options_count] = -1;

    options_count = 0;

    argc--;
    argv++;
    while(argc)
        {
        if (**argv != '/' &&
            **argv != '-')
            {
            printf("Invalid switch: %s\n", *argv);
            argc--;
            argv++;
            }
        else
            {
            option = argv[0][1];
            argc--;
            argv++;

            // Most switches require a second command line arg.
            if (argc < 1)
                fMissingParm = 1;

            switch(option)
                {
                case 'e':
                    Endpoint = *argv;
                    argc--;
                    argv++;
                    break;
                case 't':
                    Protseq = *argv;
                    argc--;
                    argv++;
                    break;
                case 's':
                    NetworkAddr = *argv;
                    argc--;
                    argv++;
                    break;
                case 'i':
                    Iterations = atoi(*argv);
                    argc--;
                    argv++;
                    if (Iterations == 0)
                        Iterations = 10;
                    break;
                case 'm':
                    MinThreads = atoi(*argv);
                    argc--;
                    argv++;
                    if (MinThreads == 0)
                        MinThreads = 1;
                    break;
                case 'a':
                    AuthnLevelStr = *argv;
                    argc--;
                    argv++;
                    break;
                case 'n':
                    if (options_count < 7)
                        {
                        Options[options_count] = atoi(*argv);
                        options_count++;
                        }
                    else
                        printf("Maximum of seven -n switchs, extra ignored.\n");
                    argc--;
                    argv++;
                    break;
                case 'l':
                    LogFileName = *argv;
                    argc--;
                    argv++;
                    break;
#ifdef __RPC_WIN16__
                case 'y':
                    RpcWinSetYieldInfo(0, FALSE, 0, 0);
                    fMissingParm = 0;
                    break;
#endif

                case 'z':
                    if (totalsize_count < 9)
                        {
                        TotalSizes[totalsize_count++] = atol(*argv);
                        TotalSizes[totalsize_count] = 0;
                        }
                    else printf("Maximum of 9 -z switches, extra ignored.\n");
                    argc--;
                    argv++;
                    break;
                case 'c':
                    if (chunksize_count < 9)
                        {
                        ChunkSizes[chunksize_count++] = atoi(*argv);
                        ChunkSizes[chunksize_count] = 0;
                        }
                    else printf("Maximum of 9 -c switches, extra ignored.\n");
                    argc--;
                    argv++;
                    break;
                case 'd':
                    pFtpRoot = *argv;
                    argc--;
                    argv++;
                    break;
                case 'w':
                    pWwwRoot = *argv;
                    argc--;
                    argv++;
                    break;

                default:
                    fMissingParm = 0;
                    printf("Usage: %s: %s\n", Name, USAGE);
                    exit(0);
                    break;
                }

            if (fMissingParm)
                {
                printf("Invalid switch %s, missing required parameter\n", *argv);
                }
            }
        } // while argc

    // determine the security level
    if (strcmp("none", AuthnLevelStr) == 0)
        AuthnLevel = RPC_C_AUTHN_LEVEL_NONE;
    else if (strcmp("connect", AuthnLevelStr) == 0)
        AuthnLevel = RPC_C_AUTHN_LEVEL_CONNECT;
    else if (strcmp("call", AuthnLevelStr) == 0)
        AuthnLevel = RPC_C_AUTHN_LEVEL_CALL;
    else if (strcmp("pkt", AuthnLevelStr) == 0)
        AuthnLevel = RPC_C_AUTHN_LEVEL_PKT;
    else if (strcmp("integrity", AuthnLevelStr) == 0)
        AuthnLevel = RPC_C_AUTHN_LEVEL_PKT_INTEGRITY;
    else if (strcmp("privacy", AuthnLevelStr) == 0)
        AuthnLevel = RPC_C_AUTHN_LEVEL_PKT_PRIVACY;
    else
        {
        printf("%s is NOT a valid authentication level, default is NONE\n", AuthnLevelStr);
        }
#if 0
    printf("Config: %s:%s[%s]\n"
           "Iterations: %d\n"
           "Server threads %d\n"
           "Options: %d %d %d\n",
           Protseq, NetworkAddr, Endpoint, Iterations, MinThreads,
           Options[0], Options[1], Options[2]);
#endif

#ifdef WIN32
    printf("Process ID: %d\n", GetCurrentProcessId());
#else
#endif

    return;
}

/////////////////////////////////////////////////////////////////////
// Figure out what we're testing and start listening.
// 

int __cdecl
main (int argc, char **argv)
{
    unsigned int MinThreads;
    unsigned long i, status;
    RPC_BINDING_VECTOR *pBindingVector;

    DTParseArgv(argc, argv);

    MinThreads = 3;
    ClientsLeft = Clients = 1;
    ActiveClients = 0;
    Iterations = 1;

    if (Options[0] > 0) ClientsLeft = Clients = Options[0];     //  Clients

    Results = MIDL_user_allocate(4 * Clients);

    if (Options[1] > 0) MinThreads = Options[1];                //  MinThreads

    if (Options[2] < 0)
        {
        memset(TestCases, 1, TEST_MAX);     //  Default: Run all tests

        //
        //  HTTP PUT doesn't work.  Don't do these tests by default.
        //
        TestCases[RECV_BUFFER_HTTP] = 0;
        TestCases[RECV_FILE_HTTP] = 0;

        //
        //  Don't do internet tests if the
        //  -d or the -w switches aren't set appropriately
        //
        if (NULL == pFtpRoot)
            {
            TestCases[SEND_BUFFER_FTP1] = 0;
            TestCases[RECV_BUFFER_FTP1] = 0;
            TestCases[SEND_FILE_FTP] = 0;
            TestCases[RECV_FILE_FTP] = 0;
            TestCases[SEND_FILE_FTP1] = 0;
            TestCases[RECV_FILE_FTP1] = 0;
            }
        if (NULL == pWwwRoot)
            {
            TestCases[SEND_BUFFER_HTTP] = 0;
            TestCases[RECV_BUFFER_HTTP] = 0;
            TestCases[SEND_FILE_HTTP] = 0;
            TestCases[RECV_FILE_HTTP] = 0;
            }
        }
    else                                    //  else run specified tests
        {
        memset(TestCases, 0, TEST_MAX);
        for(i = 2; i < 7; i++)
            {
            if ( (Options[i] < 0) || (Options[i] >= TEST_MAX) ) break;
            TestCases[Options[i]] = 1;
            }
        }

    //
    //  See make sure that there are tests to run
    //  and set CurrentCase to the first one.
    //
    CurrentCase = 0;
    for(i = 0; i < TEST_MAX; i++)
        {
        if (TestCases[i])
            {
            CurrentCase = i;
            break;
            }
        }

    if ( (pFileName == 0) && (i == TEST_MAX))
        {
        printf("No test cases selected!\n");
        return 1;
        }
    if (ChunkSizes[0] == 0)
        {
        printf("Chunk Size must be non-zero.\n");
        return 1;
        }
    if ( (pFileName == 0) && (TotalSizes[0] == 0))
        {
        printf("Total Size must be non-zero.\n");
        return 1;
        }

    CurrentIter = Iterations;
    if (CurrentIter == 0) CurrentIter = 1;

    CurrentChunkIndex = 0;
    CurrentTotalIndex = 0;

    InitializeCriticalSection(&CritSec);

    GoEvent = CreateEvent(0,
                          TRUE,
                          FALSE,
                          0);

    DoneEvent = CreateEvent(0,
                            TRUE,
                            FALSE,
                            0);

    //
    // Actually start the server
    //

    printf("FTP Directory: %s\n", (0 == pFtpRoot ? "<NONE>": pFtpRoot));
    printf("WWW Directory: %s\n", (0 == pWwwRoot ? "<NONE>": pWwwRoot));

    if (Endpoint)
        {
        status = RpcServerUseProtseqEp(Protseq, 100, Endpoint, 0);
        CHECK_STATUS(status, "RpcServerUseProtseqEp");
        }
    else
        {
        char *string_binding;

        status = RpcServerUseProtseq(Protseq, 100, 0);
        CHECK_STATUS(status, "RpcServerUseProtseqEp");

        status = RpcServerInqBindings(&pBindingVector);
        CHECK_STATUS(status, "RpcServerInqBindings");

        status = RpcEpRegister(DataTranPerf_v1_0_s_ifspec,
                               pBindingVector,
                               0,
                               0);
        CHECK_STATUS(status, "RpcEpRegister");

        status = RpcBindingToStringBinding(pBindingVector->BindingH[0],
                                           &string_binding);
        CHECK_STATUS(status, "RpcBindingToStringBinding");

        status = RpcStringBindingParse(string_binding,
                                       0, 0, 0, &Endpoint, 0);

        CHECK_STATUS(status, "RpcStringBindingParse");
        printf("Listening to %s:[%s]\n\n", Protseq, Endpoint);
        }

    status = RpcServerRegisterIf(DataTranPerf_v1_0_s_ifspec,0,0);
    CHECK_STATUS(status, "RpcServerRegisterIf");

    status = RpcServerRegisterAuthInfo(NULL,
                                       RPC_C_AUTHN_WINNT,
                                       NULL,
                                       NULL);
    CHECK_STATUS(status, "RpcServerRegisterAuthInfo");

    printf("Base Iterations: %d, Clients %d, MinThreads %d\n",
           Iterations,
           Clients,
           MinThreads);

    printf("Server listening\n");

    status = RpcServerListen(MinThreads, 100, 0);
    CHECK_STATUS(status, "RpcServerListen");

    printf("This doesn't stop listening..hmm\n");
}

/********************************************************************
 *  Control APIs that the client(s) call to sync on test cases, iterations
 *  and to report results.
 ********************************************************************/

error_status_t
BeginTest(handle_t b,
          long *ClientId)
{
    long status = 0;
    EnterCriticalSection(&CritSec);

    if (ActiveClients < Clients)
        {
        ActiveClients++;
        *ClientId = ActiveClients;
        }
    else
        {
        status = PERF_TOO_MANY_CLIENTS;
        }
    LeaveCriticalSection(&CritSec);

    return status;
}

//---------------------------------------------------------
error_status_t
NextTest(handle_t b,
         TEST_TYPE *Test,
         long *Iters,
         long *Length,
         long *ChunkSize)
{
    long wait   = 1;
    long done   = 0;
    int i;

    EnterCriticalSection(&CritSec);

    *Test = CurrentCase;
    *Iters = CurrentIter;
    *ChunkSize = ChunkSizes[CurrentChunkIndex];
    *Length = TotalSizes[CurrentTotalIndex];

    ClientsLeft--;

    if (CurrentCase == TEST_MAX)
        {
        done = 1;
        }

    if (ClientsLeft == 0)
        {
        //
        // Let all the waiting clients go
        //
        wait = 0;
        ResetEvent(DoneEvent);
        SetEvent(GoEvent);
        }

    LeaveCriticalSection(&CritSec);

    if (wait)
        {
        WaitForSingleObject(GoEvent, INFINITE);
        }

    if (done)
        {

        if (ClientsLeft == 0)
            {
            // I'm the last client, sleep and then reset for
            // a new set of clients.  Sleep avoids a race (usually).

            Sleep(1000);

            //
            //  Reset CurrentCase
            //
            for(i = 0; i < TEST_MAX; i++)
                {
                if (TestCases[i])
                    {
                    CurrentCase = i;
                    break;
                    }
                }

            CurrentIter = Iterations;
            if (CurrentIter == 0) CurrentIter = 1;

            ActiveClients = 0;
            ClientsLeft   = Clients;

            ResetEvent(GoEvent);
            ResetEvent(DoneEvent);
            }

        return PERF_TESTS_DONE;
        }

    return 0;
}

//---------------------------------------------------------
error_status_t
EndTest(handle_t b,
        unsigned long mseconds)
{
    long status, i;
    long wait = 1;
    char szTempString[80];

    EnterCriticalSection(&CritSec);

    Results[ClientsLeft] = mseconds;

    ClientsLeft++;

    if (ClientsLeft == Clients)
        {
        // All clients have finished

        // Report results

        if (pFileName == 0)
            {
            sprintf(szTempString,
                    TestNames[CurrentCase],
                    TotalSizes[CurrentTotalIndex],
                    ChunkSizes[CurrentChunkIndex]),
            printf("| % 3d | %-40s | % 4d |",
                   CurrentCase,
                   szTempString,
                   CurrentIter
                   );
            }
        else
            {
            printf("Liar!  The filename option ain't implemented yet");
            exit(0);
            }

        for(i = 0; i < Clients; i++)
            printf(" % 7d.%03d |",
                   Results[i] / CurrentIter,
                   Results[i] % CurrentIter * 1000 / CurrentIter
                   );

        printf("\n");

        //
        //  Find next case...
        //
        for(i = CurrentCase + 1; i < TEST_MAX; i++)
            if (TestCases[i])
                {
                CurrentCase = i;
                break;
                }
        if (i == TEST_MAX)
            {
            if (ChunkSizes[++CurrentChunkIndex] == 0)
                {
                CurrentChunkIndex = 0;
    
                if (TotalSizes[++CurrentTotalIndex] == 0)
                    {
                    //
                    //  This tells NextTest that we're done.
                    //
                    CurrentCase = TEST_MAX;

                    printf("TEST DONE\n");
                    }
                }
            if (TEST_MAX != CurrentCase)
                {
                int i;
                //
                //  Go back to the first case.
                //
                for(i = 0; i < TEST_MAX; i++)
                    {
                    if (TestCases[i])
                        {
                        CurrentCase = i;
                        break;
                        }
                    }
                }
            }

        CurrentIter = Iterations;
        if (CurrentIter == 0) CurrentIter = 1;

        //
        // We're setup for the next test (or to finish) let the clients go.
        //

        wait = 0;
        ResetEvent(GoEvent);
        SetEvent(DoneEvent);

        }
    LeaveCriticalSection(&CritSec);

    if (wait)
        WaitForSingleObject(DoneEvent, INFINITE);

    return 0;
}

//---------------------------------------------------------
//
// For fixed endpoint and re-bind test case
//
unsigned char *GetFixedEp(handle_t h)
{
    char *r;

    r = malloc(strlen(Endpoint) + 1);
    strcpy(r, Endpoint);
    return (unsigned char *)r;
}

void GetServerName (handle_t b,
                    unsigned long int BufferSize,
                    unsigned char *szServer)
{
    if (FALSE == GetComputerName (szServer, &BufferSize))
        {
        *szServer = 0;
        }
}


//===================================================================
//  File Context Stuff
//===================================================================
typedef struct
{
    HANDLE  hFile;
    TCHAR   FileName[MAX_PATH];
} DT_S_FILE_HANDLE;

//---------------------------------------------------------
DT_FILE_HANDLE RemoteOpen (handle_t b, unsigned long ulLength)
/*++
    FUNCTION:    RemoteOpen
    DESCRIPTION: Opens a temporary file and return a handle to client
--*/
{
    DT_S_FILE_HANDLE   *pFileContext = NULL;

    pFileContext = (DT_S_FILE_HANDLE *)MIDL_user_allocate(sizeof(DT_S_FILE_HANDLE));
    if (pFileContext == NULL)
        {
        printf("RemoteOpen: Out of memory!\n");
        return (DT_FILE_HANDLE) NULL;
        }

    //
    //  If Length is zero, the file is opened for receive from the client.
    //  So we use the prefix FSR (Server Receive); otherwise we use FSS.
    //
    CreateTempFile (NULL,
                    (0 == ulLength ? TEXT("FSR") : TEXT("FSS")),
                    ulLength,
                    (LPTSTR) pFileContext->FileName);

    pFileContext->hFile = CreateFile ((LPTSTR) pFileContext->FileName,
                                      (ulLength == 0 ? GENERIC_WRITE : GENERIC_READ),
                                      0,
                                      (LPSECURITY_ATTRIBUTES) NULL,
                                      OPEN_EXISTING,
                                      FILE_ATTRIBUTE_NORMAL,
                                      (HANDLE) NULL);

    if (pFileContext->hFile  == INVALID_HANDLE_VALUE)
        {
        printf("RemoteOpen: Cannot create temp file!\n");
        MIDL_user_free (pFileContext);
        return (DT_FILE_HANDLE) NULL;
        }

    return ((DT_FILE_HANDLE) pFileContext);
}

//---------------------------------------------------------
DT_FILE_HANDLE RemoteCreateFtpFile (handle_t        Binding,
                                    boolean         f_StoC,
                                    unsigned long   ulLength,
                                    unsigned long   ulBufferSize,
                                    unsigned char  *szRemotePath)
{
    DT_S_FILE_HANDLE   *pFileContext = NULL;
    unsigned long       iTheFileName;

    //
    //  Is the FTP Root directory specified?
    //
    if (NULL == pFtpRoot)
        {
        printf("RemoteCreateFtpFile: FTP Root Path not set.\n");
        return (DT_FILE_HANDLE) NULL;
        }

    //
    //  Allocate memory for the context handle
    //
    pFileContext = (DT_S_FILE_HANDLE *)MIDL_user_allocate(sizeof(DT_S_FILE_HANDLE));
    if (pFileContext == NULL)
        {
        printf("RemoteCreateFtpFile: Out of memory!\n");
        return (DT_FILE_HANDLE) NULL;
        }
    //
    //  This file is dealt with by the FTP server, so we don't need a handle to it.
    //  However, we need the file name so we can delete it later.
    //
    pFileContext->hFile = INVALID_HANDLE_VALUE;
    //
    //  Create a temp file to be sent to the client
    //
    if (FALSE == CreateTempFile (pFtpRoot,
                                 (TRUE == f_StoC ? TEXT("FSS") : TEXT("FSR")),
                                 (TRUE == f_StoC ? ulLength : 0),
                                 pFileContext->FileName))
        {
        MIDL_user_free (pFileContext);
        return (DT_FILE_HANDLE) NULL;
        }

    //
    //  We want to tell the client the actual filename only - no path.
    //
    for (iTheFileName = lstrlen (pFileContext->FileName)-1;
         iTheFileName>=0;
         iTheFileName--)
        {
        if (pFileContext->FileName[iTheFileName] == (TCHAR)'\\')
            {
            break;
            }
        }

    if ((ulBufferSize + iTheFileName ) < (unsigned long)lstrlen (pFileContext->FileName))
        {
        printf("RemoteCreateFtpFile: Buffer Size too small to hold path.\n");
        MIDL_user_free (pFileContext);
        return ((DT_FILE_HANDLE) NULL);
        }
    lstrcpy (szRemotePath, &(pFileContext->FileName[iTheFileName]));

    return ((DT_FILE_HANDLE) pFileContext);
}
//---------------------------------------------------------
DT_FILE_HANDLE RemoteCreateHttpFile (handle_t       Binding,
                                     boolean        f_StoC,
                                     unsigned long  ulLength,
                                     unsigned long  ulBufferSize,
                                     unsigned char *szRemotePath)
{
    DT_S_FILE_HANDLE   *pFileContext = NULL;
    unsigned long       iTheFileName;

    //
    //  Is the FTP Root directory specified?
    //
    if (NULL == pWwwRoot)
        {
        printf("RemoteCreateHttpFile: WWW Root Path not set.\n");
        return (DT_FILE_HANDLE) NULL;
        }

    //
    //  Allocate memory for the context handle
    //
    pFileContext = (DT_S_FILE_HANDLE *)MIDL_user_allocate(sizeof(DT_S_FILE_HANDLE));
    if (pFileContext == NULL)
        {
        printf("RemoteCreateHttpFile: Out of memory!\n");
        return (DT_FILE_HANDLE) NULL;
        }
    //
    //  This file is dealt with by the HTTP server, so we don't need a handle to it.
    //  However, we need the file name so we can delete it later.
    //
    pFileContext->hFile = INVALID_HANDLE_VALUE;
    //
    //  Create a temp file to be sent to the client
    //
    if (FALSE == CreateTempFile (pWwwRoot,
                                 (TRUE == f_StoC ? TEXT("FSS") : TEXT("FSR")),
                                 (TRUE == f_StoC ? ulLength : 0),
                                 pFileContext->FileName))
        {
        MIDL_user_free (pFileContext);
        return (DT_FILE_HANDLE) NULL;
        }

    //
    //  We want to tell the client the actual filename only - no path.
    //
    for (iTheFileName = lstrlen (pFileContext->FileName)-1;
         iTheFileName>=0;
         iTheFileName--)
        {
        if (pFileContext->FileName[iTheFileName] == (TCHAR)'\\')
            {
            break;
            }
        }

    if ((ulBufferSize + iTheFileName ) < (unsigned long)lstrlen (pFileContext->FileName))
        {
        printf("RemoteCreateHttpFile: Buffer Size too small to hold path.\n");
        MIDL_user_free (pFileContext);
        return ((DT_FILE_HANDLE) NULL);
        }
    lstrcpy (szRemotePath, &(pFileContext->FileName[iTheFileName]));
    if ((TCHAR)'\\' == szRemotePath[0])
        {
        szRemotePath[0] = (TCHAR)'/';
        }

    return ((DT_FILE_HANDLE) pFileContext);
}

//---------------------------------------------------------
void RemoteResetFile (DT_FILE_HANDLE phContext)
{
    assert (NULL != phContext);

    if (INVALID_HANDLE_VALUE != ((DT_S_FILE_HANDLE *)phContext)->hFile)
        {
        SetFilePointer (((DT_S_FILE_HANDLE *)phContext)->hFile,
                        0,
                        NULL,
                        FILE_BEGIN);
        }
}

//---------------------------------------------------------
void __RPC_USER DT_FILE_HANDLE_rundown(DT_FILE_HANDLE phContext)
{
    if (phContext)
        {
        if (((DT_S_FILE_HANDLE *)phContext)->hFile != INVALID_HANDLE_VALUE)
            CloseHandle(((DT_S_FILE_HANDLE *)phContext)->hFile);
        MIDL_user_free (phContext);
        }
}

//---------------------------------------------------------
void RemoteClose (DT_FILE_HANDLE *pp, boolean fDelete)
{
    assert ( (NULL != *pp) &&
             (NULL != **(DT_S_FILE_HANDLE **)pp)
           );

    if (INVALID_HANDLE_VALUE != (*(DT_S_FILE_HANDLE **)pp)->hFile)
        {
        CloseHandle((*(DT_S_FILE_HANDLE **)pp)->hFile);
        }

    if (TRUE == fDelete)
        {
        DeleteFile((*(DT_S_FILE_HANDLE **)pp)->FileName);
        }

    MIDL_user_free(*pp);
    *pp = NULL;
}

//===================================================================
//  Memory Handle stuff
//      - Each thread that uses the pipes tests will need a buffer
//      to push or pull data.  This is achieved through this memory
//      context handle.
//===================================================================
typedef struct
{
    unsigned char __RPC_FAR *pPtr;
    unsigned long ulLength;
} DT_S_MEM_HANDLE;

//---------------------------------------------------------
DT_MEM_HANDLE RemoteAllocate (handle_t h, unsigned long ulLength)
{
    DT_S_MEM_HANDLE *pMemContext = NULL;

    pMemContext = (DT_S_MEM_HANDLE *)MIDL_user_allocate(sizeof(DT_S_MEM_HANDLE));
    if (pMemContext == NULL)
        {
        printf("RemoteAllocate: Out of memory!\n");
        return (DT_FILE_HANDLE) NULL;
        }

    pMemContext->pPtr = (unsigned char __RPC_FAR *)MIDL_user_allocate(ulLength);
    if (pMemContext->pPtr == NULL)
        {
        printf("RemoteAllocate: Out of memory!\n");
        MIDL_user_free (pMemContext);
        return (DT_FILE_HANDLE) NULL;
        }

    pMemContext->ulLength = ulLength;

    return (DT_MEM_HANDLE) pMemContext;
}

//---------------------------------------------------------
void __RPC_USER DT_MEM_HANDLE_rundown(DT_MEM_HANDLE phContext)
{
    printf("DT_MEM_HANDLE_rundown Entered\n");
    if (phContext)
        {
        if (((DT_S_MEM_HANDLE *)phContext)->pPtr != NULL)
            MIDL_user_free(((DT_S_MEM_HANDLE *)phContext)->pPtr);
        MIDL_user_free (phContext);
        }
    printf("DT_MEM_HANDLE_rundown Exited\n");
}

//---------------------------------------------------------
void RemoteFree (DT_MEM_HANDLE *pMemContext)
{
    assert( ((*(DT_S_MEM_HANDLE **)pMemContext) != NULL) &&
            ((*(DT_S_MEM_HANDLE **)pMemContext)->pPtr != NULL)
          );

    //
    //  Free the block of memory
    //
    MIDL_user_free ((*(DT_S_MEM_HANDLE **)pMemContext)->pPtr);
    (*(DT_S_MEM_HANDLE **)pMemContext)->pPtr = NULL;

    //
    //  Then free the handle itself!
    //
    MIDL_user_free (*pMemContext);
    *pMemContext = (DT_MEM_HANDLE) NULL;
}

//===================================================================
//  Regular RPCs
//===================================================================

void C_to_S_Buffer (handle_t h, unsigned long int BufferSize, byte Buffer[])
{
    return;
}

//---------------------------------------------------------
void S_to_C_Buffer (handle_t h, unsigned long int BufferSize, byte Buffer[])
{
    return;
}

//---------------------------------------------------------
void C_to_S_BufferWithFile (handle_t h,
                            DT_FILE_HANDLE pContext,
                            unsigned long int BufferSize,
                            byte Buffer[])
{
    long dwBytesWritten;

    WriteFile(((DT_S_FILE_HANDLE *)pContext)->hFile,
              Buffer,
              BufferSize,
              &dwBytesWritten,
              NULL);
}

//---------------------------------------------------------
long S_to_C_BufferWithFile (handle_t h,
                            DT_FILE_HANDLE pContext,
                            unsigned long int BufferSize,
                            byte Buffer[])
{
    unsigned long dwBytesRead;

    ReadFile(((DT_S_FILE_HANDLE *)pContext)->hFile,
             Buffer,
             BufferSize,
             &dwBytesRead,
             NULL);

    return dwBytesRead;
}

//===================================================================
//  RPC Pipes
//===================================================================

void S_to_C_Pipe(handle_t       h,
                 UCHAR_PIPE     ThePipe,
                 unsigned long  ulLength,
                 DT_MEM_HANDLE  pMemHandle)
{
    unsigned long dwBytesSent;

    while (ulLength >= ((DT_S_MEM_HANDLE *)pMemHandle)->ulLength)
        {
        (ThePipe.push) (ThePipe.state,
                        ((DT_S_MEM_HANDLE *)pMemHandle)->pPtr,
                        ((DT_S_MEM_HANDLE *)pMemHandle)->ulLength);

        ulLength -= ((DT_S_MEM_HANDLE *)pMemHandle)->ulLength;
        }
    if (ulLength != 0)
        {
        (ThePipe.push) (ThePipe.state,
                        ((DT_S_MEM_HANDLE *)pMemHandle)->pPtr,
                        ulLength);
        }
    (ThePipe.push) (ThePipe.state,
                    ((DT_S_MEM_HANDLE *)pMemHandle)->pPtr,
                    0);
}

//---------------------------------------------------------
void C_to_S_Pipe (handle_t      h,
                  UCHAR_PIPE    ThePipe,
                  DT_MEM_HANDLE pMemHandle)
{
    unsigned long nBytesReceived;

    for(;;)
        {
        (ThePipe.pull) (ThePipe.state,
                        ((DT_S_MEM_HANDLE *)pMemHandle)->pPtr,
                        ((DT_S_MEM_HANDLE *)pMemHandle)->ulLength,
                        &nBytesReceived);

        if (nBytesReceived == 0) break;
        }
}

//---------------------------------------------------------
void S_to_C_PipeWithFile (handle_t          h,
                          UCHAR_PIPE        ThePipe,
                          DT_FILE_HANDLE    pContext,
                          DT_MEM_HANDLE     pMemHandle)
{
    unsigned long dwBytesRead;

    for(;;)
        {
        ReadFile(((DT_S_FILE_HANDLE *)pContext)->hFile,
                 ((DT_S_MEM_HANDLE *)pMemHandle)->pPtr,
                 ((DT_S_MEM_HANDLE *)pMemHandle)->ulLength,
                 &dwBytesRead,
                 NULL);

        (ThePipe.push) (ThePipe.state,
                        ((DT_S_MEM_HANDLE *)pMemHandle)->pPtr,
                        dwBytesRead);

        if (dwBytesRead == 0) break;
        }
}

//---------------------------------------------------------
void C_to_S_PipeWithFile (handle_t          h,
                          UCHAR_PIPE        ThePipe,
                          DT_FILE_HANDLE    pContext,
                          DT_MEM_HANDLE     pMemHandle)
{
    unsigned long nBytesReceived;
    unsigned long dwBytesWritten;

    for(;;)
        {
        (ThePipe.pull) (ThePipe.state,
                        ((DT_S_MEM_HANDLE *)pMemHandle)->pPtr,
                        ((DT_S_MEM_HANDLE *)pMemHandle)->ulLength,
                        &nBytesReceived);

        if (nBytesReceived == 0) break;

        WriteFile(((DT_S_FILE_HANDLE *)pContext)->hFile,
                  ((DT_S_MEM_HANDLE *)pMemHandle)->pPtr,
                  nBytesReceived,
                  &dwBytesWritten,
                  NULL);
        }
}