2025-04-27 07:49:33 -04:00

932 lines
19 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1999-1999 Microsoft Corporation
Module Name:
tperf.c
Abstract:
UL Performance Test Server.
Author:
Keith Moore (keithmo) 13-Sep-1999
Revision History:
--*/
#include "precomp.h"
//
// Private constants.
//
#define PENDING_IO_COUNT 8
#define NUM_HASH_BUCKETS 17
#define HASH_CONNECTION(connid) \
((HASH_SCRAMBLE(connid) + HASH_SCRAMBLE(connid >> 32)) % NUM_HASH_BUCKETS)
//
// Private types.
//
typedef
ULONG
(* PCOMPLETION_ROUTINE)(
IN struct _MY_OVERLAPPED *pMyOverlapped,
IN ULONG ErrorCode,
IN ULONG BytesTransferred
);
typedef struct _GLOBALS
{
HANDLE ControlChannel;
HANDLE AppPool;
HTTP_CONFIG_GROUP_ID ConfigGroup;
CRITICAL_SECTION HashLock;
LIST_ENTRY HashBucketListHead[NUM_HASH_BUCKETS];
ULONG RootFileNameLength;
WCHAR RootFileName[FILENAME_BUFFER_LENGTH];
} GLOBALS, *PGLOBALS;
typedef struct _MY_OVERLAPPED
{
OVERLAPPED Overlapped;
struct _IO_CONTEXT *pIoContext;
PCOMPLETION_ROUTINE pCompletionRoutine;
} MY_OVERLAPPED, *PMY_OVERLAPPED;
typedef struct _IO_CONTEXT
{
MY_OVERLAPPED Read;
MY_OVERLAPPED Send;
PHTTP_REQUEST pRequestBuffer;
HTTP_RESPONSE Response;
HTTP_DATA_CHUNK DataChunk;
PWCHAR pFileNamePart;
ULONG RequestBufferLength;
UCHAR StaticRequestBuffer[REQUEST_LENGTH];
WCHAR FileNameBuffer[FILENAME_BUFFER_LENGTH];
} IO_CONTEXT, *PIO_CONTEXT;
typedef struct _HASH_ENTRY
{
LIST_ENTRY HashBucketListEntry;
HTTP_CONNECTION_ID ConnectionId;
MY_OVERLAPPED Disconnect;
} HASH_ENTRY, *PHASH_ENTRY;
//
// Private prototypes.
//
ULONG
LocalInitialize(
VOID
);
VOID
LocalTerminate(
IN BOOLEAN TerminateUl
);
PIO_CONTEXT
AllocateIoContext(
VOID
);
VOID
InitializeIoContext(
IN PIO_CONTEXT pIoContext
);
VOID
FreeIoContext(
IN PIO_CONTEXT pIoContext
);
VOID
CleanupIoContext(
IN PIO_CONTEXT pIoContext
);
ULONG
GrowRequestBuffer(
IN PIO_CONTEXT pIoContext,
IN ULONG RequestBufferLength
);
ULONG
ReadRequest(
IN PIO_CONTEXT pIoContext,
IN HTTP_REQUEST_ID RequestId
);
ULONG
ReadRequestComplete(
IN PMY_OVERLAPPED pOverlapped,
IN ULONG ErrorCode,
IN ULONG BytesTransferred
);
ULONG
SendResponse(
IN PIO_CONTEXT pIoContext
);
ULONG
SendResponseComplete(
IN PMY_OVERLAPPED pOverlapped,
IN ULONG ErrorCode,
IN ULONG BytesTransferred
);
ULONG
WaitForDisconnect(
IN PIO_CONTEXT pIoContext,
IN HTTP_CONNECTION_ID ConnectionId
);
ULONG
WaitForDisconnectComplete(
IN PMY_OVERLAPPED pOverlapped,
IN ULONG ErrorCode,
IN ULONG BytesTransferred
);
VOID
CALLBACK
IoCompletionRoutine(
IN ULONG ErrorCode,
IN ULONG BytesTransferred,
IN LPOVERLAPPED pOverlapped
);
PHASH_ENTRY
FindHashEntryForConnection(
IN HTTP_CONNECTION_ID ConnectionId,
IN BOOLEAN OkToCreate
);
VOID
RemoveHashEntry(
IN PHASH_ENTRY pHashEntry
);
//
// Private globals.
//
DEFINE_COMMON_GLOBALS();
GLOBALS Globals;
INT
__cdecl
wmain(
INT argc,
PWSTR argv[]
)
{
ULONG result;
ULONG i;
PIO_CONTEXT pIoContext;
//
// Initialize.
//
result = CommonInit();
if (result != NO_ERROR)
{
wprintf( L"CommonInit() failed, error %lu\n", result );
return 1;
}
if (!ParseCommandLine( argc, argv ))
{
return 1;
}
result = LocalInitialize();
if (result != NO_ERROR)
{
wprintf( L"LocalInitialize() failed, error %lu\n", result );
return 1;
}
//
// Fire off the initial I/O requests.
//
for (i = 0 ; i < PENDING_IO_COUNT ; i++)
{
pIoContext = AllocateIoContext();
if (pIoContext == NULL)
{
wprintf( L"Cannot allocate IO_CONTEXT\n" );
return 1;
}
result = ReadRequest( pIoContext, HTTP_NULL_ID );
if (result != NO_ERROR && result != ERROR_IO_PENDING)
{
wprintf( L"ReadRequest() failed, error %lu\n", result );
return 1;
}
}
//
// Wait forever...
//
Sleep( INFINITE );
//
// BUGBUG: Need some way to get past the sleep...
//
LocalTerminate( TRUE );
return 0;
} // wmain
//
// Private functions.
//
ULONG
LocalInitialize(
VOID
)
{
ULONG result;
ULONG i;
HTTP_CONFIG_GROUP_APP_POOL configAppPool;
HTTP_CONFIG_GROUP_STATE configState;
HTTP_ENABLED_STATE controlState;
BOOLEAN initDone;
SECURITY_ATTRIBUTES securityAttributes;
//
// Setup globals & locals so we know how to cleanup on exit.
//
Globals.ControlChannel = NULL;
Globals.AppPool = NULL;
Globals.ConfigGroup = HTTP_NULL_ID;
initDone = FALSE;
//
// Initialize the hash data.
//
InitializeCriticalSection( &Globals.HashLock );
for (i = 0 ; i < NUM_HASH_BUCKETS ; i++)
{
InitializeListHead( &Globals.HashBucketListHead[i] );
}
//
// Get UL started.
//
result = InitUlStuff(
&Globals.ControlChannel,
&Globals.AppPool,
NULL, // FilterChannel
&Globals.ConfigGroup,
TRUE, // AllowSystem
TRUE, // AllowAdmin
FALSE, // AllowCurrentUser
FALSE, // AllowWorld
HTTP_OPTION_OVERLAPPED,
FALSE, // EnableSsl
FALSE // EnableRawFilters
);
if (result != NO_ERROR)
{
wprintf( L"InitUlStuff() failed, error %lu\n", result );
goto fatal;
}
initDone = TRUE;
//
// Get the local directory and build part of the fully canonicalized
// NT path. This makes it a bit faster to build the physical file name
// down in the request/response loop.
//
GetCurrentDirectoryW( MAX_PATH, Globals.RootFileName );
if (Globals.RootFileName[wcslen(Globals.RootFileName) - 1] == L'\\' )
{
Globals.RootFileName[wcslen(Globals.RootFileName) - 1] = L'\0';
}
Globals.RootFileNameLength = wcslen(Globals.RootFileName);
//
// Associate the app pool with an I/O completion port buried down
// in the thread pool package.
//
if (!BindIoCompletionCallback(
Globals.AppPool,
&IoCompletionRoutine,
0
))
{
result = GetLastError();
wprintf( L"BindIoCompletionCallback() failed, error %lu\n", result );
goto fatal;
}
//
// Success!
//
return NO_ERROR;
fatal:
LocalTerminate( initDone );
return result;
} // LocalInitialize
VOID
LocalTerminate(
IN BOOLEAN TerminateUl
)
{
ULONG result;
ULONG i;
PLIST_ENTRY pListEntry;
PHASH_ENTRY pHashEntry;
if (Globals.ConfigGroup != HTTP_NULL_ID)
{
result = HttpDeleteConfigGroup(
Globals.ControlChannel,
Globals.ConfigGroup
);
if (result != NO_ERROR)
{
wprintf( L"HttpDeleteConfigGroup() failed, error %lu\n", result );
}
}
if (Globals.AppPool != NULL)
{
CloseHandle( Globals.AppPool );
}
if (Globals.ControlChannel != NULL)
{
CloseHandle( Globals.ControlChannel );
}
if (TerminateUl)
{
HttpTerminate();
}
for (i = 0 ; i < NUM_HASH_BUCKETS ; i++)
{
while (!IsListEmpty( &Globals.HashBucketListHead[i] ))
{
pListEntry = RemoveHeadList( &Globals.HashBucketListHead[i] );
pHashEntry = CONTAINING_RECORD(
pListEntry,
HASH_ENTRY,
HashBucketListEntry
);
FREE( pHashEntry );
}
}
DeleteCriticalSection( &Globals.HashLock );
} // LocalTerminate
PIO_CONTEXT
AllocateIoContext(
VOID
)
{
PIO_CONTEXT pIoContext;
pIoContext = ALLOC( sizeof(*pIoContext) );
if (pIoContext != NULL)
{
InitializeIoContext( pIoContext );
}
return pIoContext;
} // AllocateIoContext
VOID
InitializeIoContext(
IN PIO_CONTEXT pIoContext
)
{
ZeroMemory( pIoContext, sizeof(*pIoContext) );
pIoContext->pRequestBuffer = (PHTTP_REQUEST)pIoContext->StaticRequestBuffer;
pIoContext->RequestBufferLength = sizeof(pIoContext->StaticRequestBuffer);
wcscpy( pIoContext->FileNameBuffer, Globals.RootFileName );
pIoContext->pFileNamePart = pIoContext->FileNameBuffer + Globals.RootFileNameLength;
pIoContext->Read.pIoContext = pIoContext;
pIoContext->Read.pCompletionRoutine = &ReadRequestComplete;
pIoContext->Send.pIoContext = pIoContext;
pIoContext->Send.pCompletionRoutine = &SendResponseComplete;
} // InitializeIoContext
VOID
FreeIoContext(
IN PIO_CONTEXT pIoContext
)
{
CleanupIoContext( pIoContext );
FREE( pIoContext );
} // FreeIoContext
VOID
CleanupIoContext(
IN PIO_CONTEXT pIoContext
)
{
if (pIoContext->pRequestBuffer != (PHTTP_REQUEST)pIoContext->StaticRequestBuffer)
{
FREE( pIoContext->pRequestBuffer );
pIoContext->pRequestBuffer = (PHTTP_REQUEST)pIoContext->StaticRequestBuffer;
}
} // CleanupIoContext
ULONG
GrowRequestBuffer(
IN PIO_CONTEXT pIoContext,
IN ULONG RequestBufferLength
)
{
PHTTP_REQUEST pNewBuffer;
if (RequestBufferLength <= pIoContext->RequestBufferLength)
{
return NO_ERROR;
}
pNewBuffer = ALLOC( RequestBufferLength );
if (pNewBuffer != NULL)
{
if (pIoContext->pRequestBuffer != (PHTTP_REQUEST)pIoContext->StaticRequestBuffer)
{
FREE( pIoContext->pRequestBuffer );
}
pIoContext->pRequestBuffer = pNewBuffer;
return NO_ERROR;
}
return ERROR_NOT_ENOUGH_MEMORY;
} // GrowRequestBuffer
ULONG
ReadRequest(
IN PIO_CONTEXT pIoContext,
IN HTTP_REQUEST_ID RequestId
)
{
ULONG result;
ULONG bytesRead;
//
// Read a request.
//
result = HttpReceiveHttpRequest(
Globals.AppPool,
RequestId,
0,
pIoContext->pRequestBuffer,
pIoContext->RequestBufferLength,
&bytesRead,
&pIoContext->Read.Overlapped
);
if (result != ERROR_IO_PENDING)
{
result = (pIoContext->Read.pCompletionRoutine)(
&pIoContext->Read,
result,
bytesRead
);
}
return result;
} // ReadRequest
ULONG
ReadRequestComplete(
IN PMY_OVERLAPPED pOverlapped,
IN ULONG ErrorCode,
IN ULONG BytesTransferred
)
{
PIO_CONTEXT pIoContext;
PHASH_ENTRY pHashEntry;
pIoContext = pOverlapped->pIoContext;
if (ErrorCode == ERROR_MORE_DATA)
{
//
// Request buffer too small; reallocate & try again.
//
ErrorCode = GrowRequestBuffer( pIoContext, BytesTransferred );
if (ErrorCode == NO_ERROR)
{
ErrorCode = ReadRequest(
pIoContext,
pIoContext->pRequestBuffer->RequestId
);
}
}
if (ErrorCode == NO_ERROR)
{
pHashEntry = FindHashEntryForConnection(
pIoContext->pRequestBuffer->ConnectionId,
FALSE
);
if (pHashEntry == NULL)
{
WaitForDisconnect(
pIoContext,
pIoContext->pRequestBuffer->ConnectionId
);
}
ErrorCode = SendResponse( pIoContext );
}
return ErrorCode;
} // ReadRequestComplete
ULONG
SendResponse(
IN PIO_CONTEXT pIoContext
)
{
ULONG result;
ULONG bytesSent;
ULONG i;
ULONG urlLength;
PWSTR url;
PWSTR tmp;
//
// Dump it.
//
if (TEST_OPTION(Verbose))
{
DumpHttpRequest( pIoContext->pRequestBuffer );
}
//
// Build the response.
//
INIT_RESPONSE( &pIoContext->Response, 200, "OK" );
url = pIoContext->pRequestBuffer->CookedUrl.pFullUrl;
urlLength = pIoContext->pRequestBuffer->CookedUrl.FullUrlLength;
//
// Hack: Find the port number, then skip to the following slash.
//
tmp = wcschr( url, L':' );
if (tmp != NULL)
{
tmp = wcschr( tmp, L'/' );
}
if (tmp != NULL)
{
tmp = wcschr( tmp, L':' );
}
if (tmp != NULL)
{
tmp = wcschr( tmp, L'/' );
}
if (tmp != NULL)
{
urlLength -= (ULONG)( (tmp - url) * sizeof(WCHAR) );
url = tmp;
}
//
// Map it into the filename.
//
for (i = 0 ; i < (urlLength/sizeof(WCHAR)) ; url++, i++)
{
if (*url == L'/')
{
pIoContext->pFileNamePart[i] = L'\\';
}
else
{
pIoContext->pFileNamePart[i] = *url;
}
}
pIoContext->pFileNamePart[i] = L'\0';
if (wcscmp( pIoContext->pFileNamePart, L"\\" ) == 0 )
{
wcscat( pIoContext->pFileNamePart, L"default.htm" );
}
if (TEST_OPTION(Verbose))
{
wprintf(
L"mapped URL %s to physical file %s\n",
pIoContext->pRequestBuffer->CookedUrl.pFullUrl,
pIoContext->FileNameBuffer
);
}
pIoContext->DataChunk.DataChunkType = HttpDataChunkFromFileName;
pIoContext->DataChunk.FromFileName.FileNameLength = wcslen(pIoContext->FileNameBuffer) * sizeof(WCHAR);
pIoContext->DataChunk.FromFileName.pFileName = pIoContext->FileNameBuffer;
pIoContext->DataChunk.FromFileName.ByteRange.StartingOffset.QuadPart = 0;
pIoContext->DataChunk.FromFileName.ByteRange.Length.QuadPart = HTTP_BYTE_RANGE_TO_EOF;
//
// Send the response.
//
pIoContext->Response.EntityChunkCount = 1;
pIoContext->Response.pEntityChunks = &pIoContext->DataChunk;
result = HttpSendHttpResponse(
Globals.AppPool,
pIoContext->pRequestBuffer->RequestId,
0,
&pIoContext->Response,
NULL,
&bytesSent,
&pIoContext->Send.Overlapped,
NULL
);
if (result != ERROR_IO_PENDING)
{
result = (pIoContext->Read.pCompletionRoutine)(
&pIoContext->Send,
result,
bytesSent
);
}
return result;
} // SendResponse
ULONG
SendResponseComplete(
IN PMY_OVERLAPPED pOverlapped,
IN ULONG ErrorCode,
IN ULONG BytesTransferred
)
{
PIO_CONTEXT pIoContext;
pIoContext = pOverlapped->pIoContext;
ErrorCode = ReadRequest( pIoContext, HTTP_NULL_ID );
return ErrorCode;
} // SendResponseComplete
ULONG
WaitForDisconnect(
IN PIO_CONTEXT pIoContext,
IN HTTP_CONNECTION_ID ConnectionId
)
{
ULONG result;
PHASH_ENTRY pHashEntry;
pHashEntry = FindHashEntryForConnection( ConnectionId, TRUE );
if (pHashEntry == NULL)
{
return ERROR_NOT_ENOUGH_MEMORY;
}
pHashEntry->Disconnect.pIoContext = pIoContext;
pHashEntry->Disconnect.pCompletionRoutine = &WaitForDisconnectComplete;
//
// Wait for disconnect.
//
wprintf( L"%I64x wait for disconnect\n", pHashEntry->ConnectionId );
result = HttpWaitForDisconnect(
Globals.AppPool,
ConnectionId,
&pHashEntry->Disconnect.Overlapped
);
if (result != ERROR_IO_PENDING)
{
result = (pHashEntry->Disconnect.pCompletionRoutine)(
&pHashEntry->Disconnect,
result,
0
);
}
return result;
} // WaitForDisconnect
ULONG
WaitForDisconnectComplete(
IN PMY_OVERLAPPED pOverlapped,
IN ULONG ErrorCode,
IN ULONG BytesTransferred
)
{
PHASH_ENTRY pHashEntry;
pHashEntry = CONTAINING_RECORD(
pOverlapped,
HASH_ENTRY,
Disconnect
);
wprintf( L"%I64x disconnected\n", pHashEntry->ConnectionId );
RemoveHashEntry( pHashEntry );
return NO_ERROR;
} // WaitForDisconnectComplete
VOID
CALLBACK
IoCompletionRoutine(
IN ULONG ErrorCode,
IN ULONG BytesTransferred,
IN LPOVERLAPPED pOverlapped
)
{
PMY_OVERLAPPED pMyOverlapped;
PIO_CONTEXT pIoContext;
pMyOverlapped = CONTAINING_RECORD(
pOverlapped,
MY_OVERLAPPED,
Overlapped
);
(pMyOverlapped->pCompletionRoutine)(
pMyOverlapped,
ErrorCode,
BytesTransferred
);
} // IoCompletionRoutine
PHASH_ENTRY
FindHashEntryForConnection(
IN HTTP_CONNECTION_ID ConnectionId,
IN BOOLEAN OkToCreate
)
{
PLIST_ENTRY pListHead;
PLIST_ENTRY pListEntry;
PHASH_ENTRY pHashEntry;
pListHead = &Globals.HashBucketListHead[HASH_CONNECTION(ConnectionId)];
EnterCriticalSection( &Globals.HashLock );
pListEntry = pListHead->Flink;
pHashEntry = NULL;
while (pListEntry != pListHead)
{
pHashEntry = CONTAINING_RECORD(
pListEntry,
HASH_ENTRY,
HashBucketListEntry
);
if (pHashEntry->ConnectionId == ConnectionId)
{
break;
}
pListEntry = pListEntry->Flink;
pHashEntry = NULL;
}
if (pHashEntry == NULL)
{
if (OkToCreate)
{
pHashEntry = ALLOC( sizeof(*pHashEntry) );
if (pHashEntry != NULL)
{
InsertTailList( pListHead, &pHashEntry->HashBucketListEntry );
pHashEntry->ConnectionId = ConnectionId;
}
}
}
LeaveCriticalSection( &Globals.HashLock );
return pHashEntry;
} // FindHashEntryForConnection
VOID
RemoveHashEntry(
IN PHASH_ENTRY pHashEntry
)
{
EnterCriticalSection( &Globals.HashLock );
RemoveEntryList( &pHashEntry->HashBucketListEntry );
LeaveCriticalSection( &Globals.HashLock );
} // RemoveHashEntry