KEEPALIVEP.C

/*++ 

Copyright (c) 1997 Microsoft Corporation

Module Name: KeepAliveP.c

Abstract:

Sample ISAPI Extension demonstrating Keep-Alive with a thread pool.

--*/

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <httpext.h>
#include <stdio.h>
#include "threadpool.h"

BOOL WINAPI
DllMain(
IN HINSTANCE hinstDll,
IN DWORD fdwReason,
IN LPVOID lpvContext
)
/*++

Purpose:

Initialize the thread pool when the DLL is loaded by IIS.

Arguments:

hinstDLL - DLL instance handle
fdwReason - notification code
lpdvContext - reserved

Returns:

TRUE if notification was successfully processed by the DLL
FALSE to indicate a failure

--*/
{
BOOL fReturn = TRUE;

switch ( fdwReason ) {

case DLL_PROCESS_ATTACH:
fReturn = InitThreadPool( );
break;
}

return fReturn;
}


BOOL WINAPI
GetExtensionVersion(
OUT HSE_VERSION_INFO * pVer
)
/*++

Purpose:

This is required ISAPI Extension DLL entry point.

Arguments:

pVer - poins to extension version info structure

Returns:

always returns TRUE

--*/
{
pVer->dwExtensionVersion = MAKELONG( HSE_VERSION_MINOR, HSE_VERSION_MAJOR );
lstrcpyn(
pVer->lpszExtensionDesc,
"ISAPI Keep-Alive with Thread Pool Extension Sample",
HSE_MAX_EXT_DLL_NAME_LEN );

return TRUE;
}


DWORD WINAPI
HttpExtensionProc(
IN EXTENSION_CONTROL_BLOCK * pECB
)
/*++

Purpose:
Demonstrate usage of persistent connections serviced by a thread pool.

Arguments:

pECB - points to the extension control block

Returns:

HSE_STATUS_PENDING if request was successfully queued
HSE_SUCCESS_AND_KEEP_CONN if request was served immediately
(presumably because the queue was full)

--*/
{
DWORD dwSize;
HSE_SEND_HEADER_EX_INFO HeaderExInfo;

char szHeader[] =
"Connection: Keep-Alive\r\n"
"Content-Length: %lu\r\n"
"Content-type: text/html\r\n\r\n";

char szBusyMessage[] =
"<html> <form method=get action=KeepAliveP.dll> <input type=submit> "
"<br>pECB->ConnID=%lu <br>Server was too busy. </form></html>";

char szBuffer[4096];
char szBuffer2[4096];

EnterCriticalSection( &csQueueLock );

if ( !AddWorkQueueEntry( pECB ) ) {

//
// if ECB could not be assigned
//

LeaveCriticalSection( &csQueueLock );

sprintf( szBuffer2, szBusyMessage, pECB->ConnID );

//
// Send outgoing header
//

sprintf( szBuffer, szHeader, strlen( szBuffer2 ) );

HeaderExInfo.pszHeader = szBuffer;
HeaderExInfo.cchHeader = strlen( szBuffer );
HeaderExInfo.pszStatus = "200 OK";
HeaderExInfo.cchStatus = strlen( HeaderExInfo.pszStatus );
HeaderExInfo.fKeepConn = TRUE;


pECB->ServerSupportFunction(
pECB->ConnID,
HSE_REQ_SEND_RESPONSE_HEADER_EX,
&HeaderExInfo,
NULL,
NULL
);

//
// Send content
//

dwSize = strlen( szBuffer2 );
pECB->WriteClient( pECB->ConnID, szBuffer2, &dwSize, 0 );

return HSE_STATUS_SUCCESS_AND_KEEP_CONN;

} else {

//
// release 1 thread from the pool
//

ReleaseSemaphore( hWorkSem, 1, NULL );

LeaveCriticalSection( &csQueueLock );
}

return HSE_STATUS_PENDING;
}


BOOL WINAPI
TerminateExtension(
IN DWORD dwFlags
)
/*++

Routine Description:

This function is called when the WWW service is shutdown

Arguments:

dwFlags - HSE_TERM_ADVISORY_UNLOAD or HSE_TERM_MUST_UNLOAD

Return Value:

TRUE if extension is ready to be unloaded,
FALSE otherwise

--*/
{
return TRUE;
}



DWORD WINAPI
WorkerFunction(
IN LPVOID pvThreadNum
)
/*++

Purpose:

Worker thread function - simulates extended processing
of the HTTP request

Arguments:

pvThreadNum - thread number

Returns:

alsways returns 0

--*/
{
EXTENSION_CONTROL_BLOCK *pECB;
DWORD dwRet, dwState, dwSize, dwThreadNum;
HSE_SEND_HEADER_EX_INFO HeaderExInfo;

//This header will be filled in with the content length
char szHeader[] =
"Connection: Keep-Alive\r\nContent-Length: %lu\r\n"
"Content-type: text/html\r\n\r\n";

char szContent[] =
"<html> <form method=get action=KeepAliveP.dll><input type=submit> "
"<br>pECB->ConnID=%lu <br>dwThreadNum=%lu</form></html>";

char szBuffer[4096];
char szBuffer2[4096];

dwThreadNum = ( DWORD ) pvThreadNum;

while ( TRUE ) {

dwRet = WaitForSingleObject( hWorkSem, INFINITE );
if ( dwRet == WAIT_OBJECT_0 ) {

EnterCriticalSection( &csQueueLock );

if ( GetWorkQueueEntry( &pECB ) ) {

//
// Found work to do
//

LeaveCriticalSection( &csQueueLock );

sprintf( szBuffer2, szContent, pECB->ConnID, dwThreadNum );

// Send outgoing header
sprintf( szBuffer, szHeader, strlen( szBuffer2 ) );

HeaderExInfo.pszHeader = szBuffer;
HeaderExInfo.cchHeader = strlen( szBuffer );
HeaderExInfo.pszStatus = "200 OK";
HeaderExInfo.cchStatus = strlen( HeaderExInfo.pszStatus );
HeaderExInfo.fKeepConn = TRUE;

pECB->ServerSupportFunction(
pECB->ConnID,
HSE_REQ_SEND_RESPONSE_HEADER_EX,
&HeaderExInfo,
NULL,
NULL
);

//
// Simulate extended processing
//

Sleep( 3000 );

//
// Send content
//

dwSize = strlen( szBuffer2 );
pECB->WriteClient( pECB->ConnID, szBuffer2, &dwSize, 0 );

//
// Tell IIS to keep the connection open
//

dwState = HSE_STATUS_SUCCESS_AND_KEEP_CONN;

pECB->ServerSupportFunction(
pECB->ConnID,
HSE_REQ_DONE_WITH_SESSION,
&dwState,
NULL,
0
);

} else {

//
// No item found is unexpected condition - exit thread
//

LeaveCriticalSection( &csQueueLock );
ExitThread( 0 );
}

} else {
break;
}
}

return 0;
}