932 lines
19 KiB
C
932 lines
19 KiB
C
/*++
|
||
|
||
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
|
||
|