WORKERTHREAD.C

/*++ 

Copyright (c) 1997 Microsoft Corporation

Module Name: WorkerThread.c

Abstract:

IIS maintains a pool of threads to handle incoming HTTP requests. When all of
these threads are in use, new requests will be rejected. If all the pool threads
are in a wait state (for instance, running ISAPI dlls that are waiting for a query
on a remote database to complete), IIS may reject incoming requests even if there
is plenty of CPU power to handle them.

One way of avoiding this situation is to offload processing of these types of
requests to a worker thread, releasing the IIS thread back to the pool so that it
can be used for another request. This basic sample demonstrates how to implement
this in an ISAPI dll.

*/

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

//
// global variables
//

DWORD g_dwThreadCount = 0;

//
// local functions prototypes
//

DWORD WINAPI WorkerFunction( LPVOID );
BOOL SendHttpHeaders(EXTENSION_CONTROL_BLOCK *, LPCSTR , LPCSTR, BOOL );



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

--*/
{

//
// tell the server our version number and extension description
//

pVer->dwExtensionVersion =
MAKELONG( HSE_VERSION_MINOR, HSE_VERSION_MAJOR );

lstrcpyn(
pVer->lpszExtensionDesc,
"ISAPI Worker Thread Extension Sample",
HSE_MAX_EXT_DLL_NAME_LEN
);

return TRUE;
}


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

Purpose:

Create a thread to handle extended processing. It will be passed
the address of a function ("WorkerFunction") to run, and the address
of the ECB associated with this session.

Arguments:

pECB - pointer to the extenstion control block

Returns:

HSE_STATUS_PENDING to mark this request as pending

--*/
{
DWORD dwThreadID;


// (NOTE: in production environment you'd probably
// wanted to limit the number of threads created)

CreateThread(NULL, // Pointer to thread security attributes
0, // Initial thread stack size, in bytes
WorkerFunction, // Pointer to thread function
pECB, // The ECB is the argument for the new thread
0, // Creation flags
&dwThreadID // Pointer to returned thread identifier
);

//
// update global thread count
//

InterlockedIncrement( &g_dwThreadCount );

// Return HSE_STATUS_PENDING to release IIS pool thread without losing connection

return HSE_STATUS_PENDING;
}


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

Purpose:

Show how to perform extended processing in ISAPI DLL without
tying IIS threads.

Arguments:

vECB - points to current extension control block

Returns:

returns 0

--*/
{
char szHeader[] = "Content-type: text/html\r\n\r\n";
char szContent[]= "<html> <form method=get action=WorkerThread.dll><h1>Worker Thread Sample</h1><hr>"
"<input type=submit value=\"Send Request\"> </form></html>";
EXTENSION_CONTROL_BLOCK *pECB;
DWORD dwSize;

//
// Initialize local ECB pointer to void pointer passed to thread
//

pECB = vECB;


//
// Send outgoing header
//

SendHttpHeaders( pECB, "200 OK", szHeader, FALSE );

//
// Simulate extended processing for 5 seconds
//

Sleep(5000);


//
// Send content
//

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


//
// Inform server that the request has been satisfied,
// and the connection may now be dropped
//

pECB->ServerSupportFunction(
pECB->ConnID,
HSE_REQ_DONE_WITH_SESSION,
NULL,
NULL,
NULL
);

//
// update global thread count
//

InterlockedDecrement( &g_dwThreadCount );

return 0;
}


BOOL
SendHttpHeaders(
EXTENSION_CONTROL_BLOCK *pECB,
LPCSTR pszStatus,
LPCSTR pszHeaders,
BOOL fKeepConnection
)
/*++

Purpose:
Send specified HTTP status string and any additional header strings
using new ServerSupportFunction() request HSE_SEND_HEADER_EX_INFO

Arguments:

pECB - pointer to the extension control block
pszStatus - HTTP status string (e.g. "200 OK")
pszHeaders - any additional headers, separated by CRLFs and
terminated by empty line

Returns:

TRUE if headers were successfully sent
FALSE otherwise

--*/
{
HSE_SEND_HEADER_EX_INFO header_ex_info;
BOOL success;

header_ex_info.pszStatus = pszStatus;
header_ex_info.pszHeader = pszHeaders;
header_ex_info.cchStatus = strlen( pszStatus );
header_ex_info.cchHeader = strlen( pszHeaders );
header_ex_info.fKeepConn = fKeepConnection;


success = pECB->ServerSupportFunction(
pECB->ConnID,
HSE_REQ_SEND_RESPONSE_HEADER_EX,
&header_ex_info,
NULL,
NULL
);

return success;
}


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 when extension is ready to be unloaded,

--*/
{

// (Our threads are deterministic and will complete
// not much later than 5 seconds from now)

//
// wait for all threads to terminate, sleeping for 1 sec
//

while( g_dwThreadCount > 0 ) {
SleepEx( 1000, FALSE );
}

//
// make sure the last thread indeed exited
//

SleepEx( 1000, FALSE );

return TRUE;
}