windows-nt/Source/XPSP1/NT/inetsrv/iis/iisrearc/iisplus/ulw3/maincontext.cxx
2020-09-26 16:20:57 +08:00

2460 lines
55 KiB
C++

/*++
Copyright (c) 1999 Microsoft Corporation
Module Name :
maincontext.cxx
Abstract:
Drive the state machine
Author:
Bilal Alam (balam) 10-Mar-2000
Environment:
Win32 - User Mode
Project:
ULW3.DLL
--*/
#include "precomp.hxx"
#include "rawconnection.hxx"
#include "sspiprovider.hxx"
#include "basicprovider.hxx"
#include "servervar.hxx"
//
// Global alloc cache and context list
//
ALLOC_CACHE_HANDLER * W3_MAIN_CONTEXT::sm_pachMainContexts = NULL;
W3_STATE * W3_MAIN_CONTEXT::sm_pStates[ STATE_COUNT ];
SHORT W3_MAIN_CONTEXT::sm_rgInline[ STATE_COUNT ];
USHORT W3_MAIN_CONTEXT::sm_cbInlineBytes = 0;
LONG W3_MAIN_CONTEXT::sm_cOutstandingThreads = 0;
DWORD W3_MAIN_CONTEXT::sm_dwTimeout = 0;
VOID
W3_MAIN_CONTEXT::DoWork(
DWORD cbCompletion,
DWORD dwCompletionStatus,
BOOL fIoCompletion
)
/*++
Routine Description:
Drives the W3 state machine
Arguments:
cbCompletion - Number of bytes in an async completion
dwCompletionStatus - Error status of a completion
fIoCompletion - TRUE if this was an IO completion,
FALSE if this was a new request completion
Return Value:
None
--*/
{
CONTEXT_STATUS Status = CONTEXT_STATUS_CONTINUE;
BOOL fLastState = FALSE;
W3_CONTEXT * pCurrentContext = NULL;
if (fIoCompletion)
{
if (QueryLastIOPending() == LOG_WRITE_IO)
{
_LogContext.m_dwBytesSent += cbCompletion;
}
else if (QueryLastIOPending() == LOG_READ_IO)
{
_LogContext.m_dwBytesRecvd += cbCompletion;
if ( _cbRemainingEntityFromUL != INFINITE )
{
if ( _cbRemainingEntityFromUL >= cbCompletion )
{
_cbRemainingEntityFromUL -= cbCompletion;
}
else
{
_cbRemainingEntityFromUL = 0;
}
}
}
}
//
// Progress thru states until we are finished or a state operation
// is performed asynchronously
//
while ( !fLastState )
{
W3_STATE * pState;
//
// Get the next function to call, and then call it
//
pState = sm_pStates[ _currentState ];
DBG_ASSERT( pState != NULL );
//
// Manage the _nextState which indicates what the next state will be
// if the DoWork() returns CONTEXT_STATUS_CONTINUE. Note that this
// state can be overriden by W3_MAIN_CONTEXT::SetFinishedResponse
//
_nextState = _currentState + 1;
//
// If this is the last state, remember that so we can cleanup
//
if ( _currentState == CONTEXT_STATE_DONE )
{
fLastState = TRUE;
}
if ( !fIoCompletion )
{
Status = pState->DoWork( this,
cbCompletion,
dwCompletionStatus );
}
else
{
pCurrentContext = QueryCurrentContext();
//
// First try to complete handler contexts if any.
//
Status = pCurrentContext->ExecuteHandlerCompletion(
cbCompletion,
dwCompletionStatus );
if ( Status == CONTEXT_STATUS_CONTINUE )
{
//
// Excellent. All handlers for this context have
// completed. Now we finally complete the original
// state which originally started the async ball rolling
//
Status = pState->OnCompletion( this,
cbCompletion,
dwCompletionStatus );
}
//
// Reset fIoCompletion so we can continue the state machine
// after the completion function is done
//
fIoCompletion = FALSE;
}
//
// An async operation was posted, bail immediately
//
if ( Status == CONTEXT_STATUS_PENDING )
{
return;
}
DBG_ASSERT( Status == CONTEXT_STATUS_CONTINUE );
_currentState = _nextState;
}
//
// If we get here, we must have executed the last state, so cleanup the
// MAIN_CONTEXT
//
DBG_ASSERT( fLastState );
//
// If we have a raw connection, detach ourselves from it now
//
if ( _pRawConnection != NULL )
{
_pRawConnection->SetMainContext( NULL );
}
DereferenceMainContext();
}
VOID
W3_MAIN_CONTEXT::BackupStateMachine(
VOID
)
/*++
Routine Description:
Backup in state machine to the URL_INFO state. This should be used only
by AUTH_COMPLETE filters
Arguments:
None
Return Value:
None
--*/
{
URL_CONTEXT * pUrlContext;
DBG_ASSERT( IsNotificationNeeded( SF_NOTIFY_AUTH_COMPLETE ) );
//
// Clear the URL context
//
pUrlContext = QueryUrlContext();
DBG_ASSERT( pUrlContext != NULL );
SetUrlContext( NULL );
delete pUrlContext;
//
// Reset our access check state.
//
ResetAccessCheck();
//
// Back that state up
//
_nextState = CONTEXT_STATE_URLINFO;
}
// static
HRESULT
W3_MAIN_CONTEXT::SetupStateMachine(
VOID
)
/*++
Routine Description:
Setup state machine
Arguments:
None
Return Value:
HRESULT
--*/
{
HRESULT hr = NO_ERROR;
W3_STATE * pState = NULL;
USHORT cbContextSize = 0;
DWORD cState = CONTEXT_STATE_START;
//
// First create all the states
//
//
// Start State
//
pState = (W3_STATE*) new W3_STATE_START();
if ( pState == NULL )
{
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto Failure;
}
else if ( FAILED( hr = pState->QueryResult() ) )
{
goto Failure;
}
sm_pStates[ cState ] = pState;
cbContextSize += pState->QueryContextSize();
//
// provide space for 8 bit alignment
//
cbContextSize = (cbContextSize + 7) & ~7;
cState++;
//
// URLINFO State
//
pState = (W3_STATE*) new W3_STATE_URLINFO();
if ( pState == NULL )
{
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto Failure;
}
else if ( FAILED( hr = pState->QueryResult() ) )
{
goto Failure;
}
sm_pStates[ cState ] = pState;
cbContextSize += pState->QueryContextSize();
//
// provide space for 8 bit alignment
//
cbContextSize = (cbContextSize + 7) & ~7;
cState++;
//
// Authentication State
//
pState = (W3_STATE*) new W3_STATE_AUTHENTICATION();
if ( pState == NULL )
{
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto Failure;
}
else if ( FAILED( hr = pState->QueryResult() ) )
{
goto Failure;
}
sm_pStates[ cState ] = pState;
cbContextSize += pState->QueryContextSize();
//
// provide space for 8 bit alignment
//
cbContextSize = (cbContextSize + 7) & ~7;
cState++;
//
// Authorization State
//
pState = (W3_STATE*) new W3_STATE_AUTHORIZATION();
if ( pState == NULL )
{
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto Failure;
}
else if ( FAILED( hr = pState->QueryResult() ) )
{
goto Failure;
}
sm_pStates[ cState ] = pState;
cbContextSize += pState->QueryContextSize();
//
// provide space for 8 bit alignment
//
cbContextSize = (cbContextSize + 7) & ~7;
cState++;
//
// Handle Request State
//
pState = (W3_STATE*) new W3_STATE_HANDLE_REQUEST();
if ( pState == NULL )
{
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto Failure;
}
else if ( FAILED( hr = pState->QueryResult() ) )
{
goto Failure;
}
sm_pStates[ cState ] = pState;
cbContextSize += pState->QueryContextSize();
//
// provide space for 8 bit alignment
//
cbContextSize = (cbContextSize + 7) & ~7;
cState++;
//
// Response State
//
pState = (W3_STATE*) new W3_STATE_RESPONSE();
if ( pState == NULL )
{
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto Failure;
}
else if ( FAILED( hr = pState->QueryResult() ) )
{
goto Failure;
}
sm_pStates[ cState ] = pState;
cbContextSize += pState->QueryContextSize();
//
// provide space for 8 bit alignment
//
cbContextSize = (cbContextSize + 7) & ~7;
cState++;
//
// Log State
//
pState = (W3_STATE*) new W3_STATE_LOG();
if ( pState == NULL )
{
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto Failure;
}
else if ( FAILED( hr = pState->QueryResult() ) )
{
goto Failure;
}
sm_pStates[ cState ] = pState;
cbContextSize += pState->QueryContextSize();
//
// provide space for 8 bit alignment
//
cbContextSize = (cbContextSize + 7) & ~7;
cState++;
//
// Done State
//
pState = (W3_STATE*) new W3_STATE_DONE();
if ( pState == NULL )
{
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto Failure;
}
else if ( FAILED( hr = pState->QueryResult() ) )
{
goto Failure;
}
sm_pStates[ cState ] = pState;
cbContextSize += pState->QueryContextSize();
//
// provide space for 8 bit alignment
//
cbContextSize = (cbContextSize + 7) & ~7;
cState++;
//
// Keep track of total number of state bytes needed so that we can
// initialize allocation cache properly
//
// throw in 8 more bytes, alignment may cause us to need it
sm_cbInlineBytes = cbContextSize + 8;
return NO_ERROR;
Failure:
for ( int i = 0; i < STATE_COUNT; i++ )
{
if ( sm_pStates[ i ] != NULL )
{
delete sm_pStates[ i ];
sm_pStates[ i ] = NULL;
}
}
return hr;
}
// static
VOID
W3_MAIN_CONTEXT::CleanupStateMachine(
VOID
)
/*++
Routine Description:
Cleanup state machine
Arguments:
None
Return Value:
None
--*/
{
for ( int i = CONTEXT_STATE_START;
i < STATE_COUNT;
i++ )
{
if ( sm_pStates[ i ] != NULL )
{
delete sm_pStates[ i ];
sm_pStates[ i ] = NULL;
}
}
}
BOOL
W3_MAIN_CONTEXT::SetupContext(
HTTP_REQUEST * pUlHttpRequest,
ULATQ_CONTEXT ulatqContext
)
/*++
Routine Description:
Sets up a MAIN_CONTEXT before executing the state machine for a new
incoming request.
Arguments:
pUlHttpRequest - the HTTP_REQUEST from UL
ulatqContext - used to send/receive data thru ULATQ
Return Value:
TRUE if successful, else FALSE
--*/
{
memset( _rgStateContexts, 0, sizeof( _rgStateContexts ) );
//
// Should we generate a content-length header
//
if ( pUlHttpRequest->Verb == HttpVerbHEAD )
{
_fGenerateContentLength = TRUE;
}
//
// Associate HTTP_REQUEST with W3_REQUEST wrapper
//
_request.SetHttpRequest( pUlHttpRequest );
//
// Associate context for async IO (if any)
//
_ulatqContext = ulatqContext;
UlAtqSetContextProperty( _ulatqContext,
ULATQ_PROPERTY_COMPLETION_CONTEXT,
this );
//
// Setup the state machine
//
_currentState = CONTEXT_STATE_START;
_nextState = CONTEXT_STATE_START;
//
// Setup current context to receive IO completions. Naturally on
// startup, this context will be 'this'. But it can change depending
// on whether child executes are called
//
_pCurrentContext = this;
return TRUE;
}
W3_CONNECTION_STATE *
W3_MAIN_CONTEXT::QueryConnectionState(
VOID
)
/*++
Routine Description:
Get any context associated with this connection and this state.
Arguments:
None
Return Value:
A W3_CONNECTION_STATE * or NULL if there is no state
--*/
{
//
// Since we are just looking for any existing connection state, make
// sure we don't create a connection object if none is already associated
// (creating a connection object is expensive)
//
W3_CONNECTION * pConn = QueryConnection( FALSE );
return pConn ? pConn->QueryConnectionState( _currentState ) : NULL;
}
VOID
W3_MAIN_CONTEXT::SetConnectionState(
W3_CONNECTION_STATE * pConnectionState
)
/*++
Routine Description:
Set any context to be associated with the connection and current state
Arguments:
pConnectionState - Context to associate
Return Value:
None
--*/
{
if ( QueryConnection() )
{
QueryConnection()->SetConnectionState( _currentState,
pConnectionState );
}
}
W3_MAIN_CONTEXT::W3_MAIN_CONTEXT(
HTTP_REQUEST * pUlHttpRequest,
ULATQ_CONTEXT ulAtqContext
)
: W3_CONTEXT ( 0 ),
_pSite ( NULL ),
_pFilterContext ( NULL ),
_fDisconnect ( FALSE ),
_fNeedFinalDone ( FALSE ),
_fAssociationChecked ( FALSE ),
_pConnection ( NULL ),
_pUrlContext ( NULL ),
_pUserContext ( NULL ),
_fProviderHandled ( FALSE ),
_cbInlineOffset ( 0 ),
_fDoneWithCompression ( FALSE ),
_pCompressionContext ( NULL ),
_fIsUlCacheable ( TRUE ),
_pCertificateContext ( NULL ),
_cbRemainingEntityFromUL ( 0 ),
_fGenerateContentLength( FALSE ),
_pRawConnection ( NULL ),
_cRefs ( 1 ),
_hTimer (NULL)
{
_LogContext.m_msStartTickCount = GetTickCount();
SetupContext( pUlHttpRequest, ulAtqContext );
_hTimer = NULL;
if (sm_dwTimeout)
{
BOOL fRet;
fRet = CreateTimerQueueTimer(&_hTimer,
NULL,
W3_MAIN_CONTEXT::TimerCallback,
this,
sm_dwTimeout,
0,
WT_EXECUTEONLYONCE
);
DBG_ASSERT(fRet);
}
}
W3_MAIN_CONTEXT::~W3_MAIN_CONTEXT()
/*++
Routine Description:
Main context destructor
Arguments:
None
Return Value:
None
--*/
{
//
// Cleanup context state
//
for ( DWORD i = 0; i < STATE_COUNT; i++ )
{
if ( _rgStateContexts[ i ] != NULL )
{
((W3_MAIN_CONTEXT_STATE*) _rgStateContexts[ i ])->Cleanup( this );
_rgStateContexts[ i ] = NULL;
}
}
//
// Let our filter context go
//
if ( _pFilterContext != NULL )
{
_pFilterContext->SetMainContext( NULL );
_pFilterContext->DereferenceFilterContext();
_pFilterContext = NULL;
}
//
// Let go of reference to associated connection
//
if ( _pConnection != NULL )
{
_pConnection->DereferenceConnection();
_pConnection = NULL;
}
//
// Cleanup URL-Context
//
if ( _pUrlContext != NULL )
{
delete _pUrlContext;
_pUrlContext = NULL;
}
//
// Release our user context
//
if ( _pUserContext != NULL )
{
// perf ctr
if (_pUserContext->QueryAuthType() == MD_AUTH_ANONYMOUS)
{
_pSite->DecAnonUsers();
}
else
{
_pSite->DecNonAnonUsers();
}
_pUserContext->DereferenceUserContext();
_pUserContext = NULL;
}
//
// Release the compression context
//
if ( _pCompressionContext != NULL )
{
delete _pCompressionContext;
_pCompressionContext = NULL;
}
//
// Cleanup RDNS crud
//
_IpAddressCheck.UnbindAddr();
//
// Cleanup client certificate context
//
if ( _pCertificateContext != NULL )
{
delete _pCertificateContext;
_pCertificateContext = NULL;
}
//
// Release the raw connection now
//
if ( _pRawConnection != NULL )
{
_pRawConnection->DereferenceRawConnection();
_pRawConnection = NULL;
}
//
// Allow ULATQ to cleanup itself up and to read the next request
//
UlAtqFreeContext( _ulatqContext );
_ulatqContext = NULL;
//
// Finally release the site
//
if ( _pSite )
{
_pSite->Release();
_pSite = NULL;
}
if (_hTimer)
{
BOOL fRet;
fRet = DeleteTimerQueueTimer(NULL,
_hTimer,
INVALID_HANDLE_VALUE);
DBG_ASSERT(fRet);
_hTimer = NULL;
}
}
// static
HRESULT
W3_MAIN_CONTEXT::Initialize(
VOID
)
/*++
Routine Description:
Global initialization routine for W3_MAIN_CONTEXTs
Arguments:
None
Return Value:
HRESULT
--*/
{
ALLOC_CACHE_CONFIGURATION acConfig;
HRESULT hr = NO_ERROR;
//
// Setup global state machine. We do this BEFORE we setup the
// allocation cache because the state machine setup will tell how much
// inline buffer space is needed for state
//
hr = SetupStateMachine();
if ( FAILED( hr ) )
{
return hr;
}
//
// Setup allocation lookaside
//
acConfig.nConcurrency = 1;
acConfig.nThreshold = 100;
acConfig.cbSize = sizeof( W3_MAIN_CONTEXT ) + sm_cbInlineBytes;
DBG_ASSERT( sm_pachMainContexts == NULL );
sm_pachMainContexts = new ALLOC_CACHE_HANDLER( "W3_MAIN_CONTEXT",
&acConfig );
if ( sm_pachMainContexts == NULL )
{
CleanupStateMachine();
return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
sm_dwTimeout = ReadRegDword(HKEY_LOCAL_MACHINE,
REGISTRY_KEY_INETINFO_PARAMETERS_W,
L"RequestTimeoutBreak",
0);
return NO_ERROR;
}
// static
VOID
W3_MAIN_CONTEXT::WaitForThreadDrain(
VOID
)
/*++
Routine Description:
Wait for all threads doing W3CORE stuff to drain away
Arguments:
None
Return Value:
None
--*/
{
while ( sm_cOutstandingThreads != 0 )
{
Sleep( 200 );
}
}
// static
VOID
W3_MAIN_CONTEXT::Terminate(
VOID
)
/*++
Routine Description:
Terminate MAIN_CONTEXT globals
Arguments:
None
Return Value:
None
--*/
{
CleanupStateMachine();
if ( sm_pachMainContexts != NULL )
{
delete sm_pachMainContexts;
sm_pachMainContexts = NULL;
}
}
W3_USER_CONTEXT *
W3_MAIN_CONTEXT::QueryConnectionUserContext(
VOID
)
/*++
Routine Description:
Get any user context associated with this connection
Arguments:
None
Return Value:
Pointer to W3_USER_CONTEXT (or NULL if no used associated)
--*/
{
W3_CONNECTION * pConnection = NULL;
pConnection = QueryConnection( FALSE );
if ( pConnection != NULL )
{
return pConnection->QueryUserContext();
}
else
{
return NULL;
}
}
VOID
W3_MAIN_CONTEXT::SetConnectionUserContext(
W3_USER_CONTEXT * pUserContext
)
/*++
Routine Description:
Associate user context with connection
Arguments:
pUserContext - User context to associate
Return Value:
None
--*/
{
W3_CONNECTION * pConnection = NULL;
pConnection = QueryConnection( TRUE );
if ( pConnection != NULL )
{
pConnection->SetUserContext( pUserContext );
}
else
{
DBG_ASSERT( FALSE );
}
}
HRESULT
W3_MAIN_CONTEXT::ReceiveEntityBody(
BOOL fAsync,
VOID * pBuffer,
DWORD cbBuffer,
DWORD * pBytesReceived
)
/*++
Routine Description:
Receives entity data from the client
Arguments:
fAsync - TRUE if this is an async request
pBuffer - The buffer to store the data
cbBuffer - The size of the buffer
pBytesReceived - Upon return, the amount of data copied
into the buffer
Return Value:
HRESULT
--*/
{
HRESULT hr = UlAtqReceiveEntityBody( QueryUlatqContext(),
fAsync,
0,
pBuffer,
cbBuffer,
pBytesReceived );
//
// Keep track of how much we're reading
//
if (!fAsync &&
SUCCEEDED(hr))
{
if ( _cbRemainingEntityFromUL != INFINITE )
{
if ( _cbRemainingEntityFromUL >= *pBytesReceived )
{
_cbRemainingEntityFromUL -= *pBytesReceived;
}
else
{
_cbRemainingEntityFromUL = 0;
}
}
}
return hr;
}
W3_CONNECTION *
W3_MAIN_CONTEXT::QueryConnection(
BOOL fCreateIfNotFound
)
/*++
Routine Description:
Get the W3_CONNECTION object associated with this request
Arguments:
fCreateIfNotFound - If not found in hash table, create it
Return Value:
Pointer to W3_CONNECTION.
--*/
{
HRESULT hr;
if ( _pConnection == NULL )
{
//
// Get the connection associated with this request
//
if ( !fCreateIfNotFound && _fAssociationChecked )
{
//
// If we have already looked for the connection, and we're not
// required to create one, then we can fast path
//
hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
}
else
{
hr = W3_CONNECTION::RetrieveConnection( _request.QueryConnectionId(),
fCreateIfNotFound,
&_pConnection );
}
if ( FAILED( hr ) )
{
if ( fCreateIfNotFound )
{
DBGPRINTF(( DBG_CONTEXT,
"Error retrieving connection. hr = %x\n",
hr ));
}
else
{
//
// Not really an error. We were just querying the hash table
// for an associated connection (but not creating one)
//
}
}
else
{
DBG_ASSERT( _pConnection != NULL );
}
//
// Don't try to repeat connection lookup again
//
_fAssociationChecked = TRUE;
}
return _pConnection;
}
VOID *
W3_MAIN_CONTEXT::ContextAlloc(
UINT cbSize
)
/*++
Routine Description:
Allocate context space from inline buffer in MAIN_CONTEXT. This
complicated mechanism allows for states to allocate small state
without going to the heap.
Arguments:
cbSize - Size to allocate
Return Value:
Pointer to buffer
--*/
{
BYTE *pOrigBuffer = (PBYTE) QueryInlineBuffer() + _cbInlineOffset;
//
// Make space for 8 byte alignment
//
VOID *pBuffer = (VOID *)(((DWORD_PTR)pOrigBuffer + 7) & ~7);
_cbInlineOffset += DIFF((PBYTE)pBuffer - pOrigBuffer);
if ( _cbInlineOffset + cbSize > sm_cbInlineBytes )
{
DBG_ASSERT( FALSE );
return NULL;
}
_cbInlineOffset += cbSize;
return pBuffer;
}
BOOL
W3_MAIN_CONTEXT::NotifyFilters(
DWORD dwNotification,
PVOID pvFilterInfo,
BOOL * pfFinished
)
/*++
Routine Description:
Notify all applicable filters for a given notification. This is a
wrapper of the W3_FILTER_CONTEXT call to actually do the work. The
notifications made in this routine are those which would occur during
the worker process state machine. (excludes end_of_net_session and
raw data notifications)
Arguments:
dwNotification - Notification in question
pvFilterInfo - Points to any info object passed to filter
pfFinished - Set to TRUE if the filter decided to complete work
Return Value:
BOOL
--*/
{
BOOL fRet = FALSE;
BOOL fSynchronized = FALSE;
DBG_ASSERT( _pFilterContext != NULL );
_pFilterContext->FilterLock();
switch( dwNotification )
{
case SF_NOTIFY_PREPROC_HEADERS:
fRet = _pFilterContext->NotifyPreProcHeaderFilters( pfFinished );
break;
case SF_NOTIFY_URL_MAP:
fRet = _pFilterContext->NotifyUrlMap( (HTTP_FILTER_URL_MAP*) pvFilterInfo,
pfFinished );
break;
case SF_NOTIFY_AUTHENTICATION:
fRet = _pFilterContext->NotifyAuthentication( (HTTP_FILTER_AUTHENT*) pvFilterInfo,
pfFinished );
break;
case SF_NOTIFY_AUTH_COMPLETE:
fRet = _pFilterContext->NotifyAuthComplete(
( HTTP_FILTER_AUTH_COMPLETE_INFO * )pvFilterInfo,
pfFinished );
break;
case SF_NOTIFY_SEND_RESPONSE:
fRet = _pFilterContext->NotifySendResponseFilters(
(HTTP_FILTER_SEND_RESPONSE*) pvFilterInfo,
pfFinished );
break;
case SF_NOTIFY_END_OF_REQUEST:
fRet = _pFilterContext->NotifyEndOfRequest();
break;
case SF_NOTIFY_LOG:
fRet = _pFilterContext->NotifyLogFilters((HTTP_FILTER_LOG *)pvFilterInfo);
break;
case SF_NOTIFY_SEND_RAW_DATA:
fRet = _pFilterContext->NotifySendRawFilters(
(HTTP_FILTER_RAW_DATA*) pvFilterInfo,
pfFinished );
break;
default:
DBG_ASSERT( FALSE );
fRet = FALSE;
}
_pFilterContext->FilterUnlock();
return fRet;
}
W3_FILTER_CONTEXT *
W3_MAIN_CONTEXT::QueryFilterContext(
BOOL fCreateIfNotFound
)
/*++
Routine Description:
Get a filter context to associate with the MAIN_CONTEXT and to also (
AAAAAAAARRRRRRRGGGGGGGHHHHHH) associate with the connection on the
W3_CONTXT
Arguments:
fCreateIfNotFound - Should we create a context if it doesn't already exist
Return Value:
Pointer to a new W3_FILTER_CONTEXT
--*/
{
BOOL fSecure;
if ( _pFilterContext == NULL &&
fCreateIfNotFound )
{
fSecure = QueryRequest()->IsSecureRequest();
DBG_ASSERT( _pSite != NULL );
_pFilterContext = new W3_FILTER_CONTEXT( fSecure,
_pSite->QueryFilterList() );
if ( _pFilterContext != NULL )
{
_pFilterContext->SetMainContext( this );
}
else
{
DBG_ASSERT( FALSE );
}
}
return _pFilterContext;
}
BOOL
W3_MAIN_CONTEXT::IsNotificationNeeded(
DWORD dwNotification
)
/*++
Routine Description:
Is a specific filter notification applicable for this request
Arguments:
dwNotification - Notification in question
Return Value:
BOOL
--*/
{
BOOL fNeeded = FALSE;
FILTER_LIST * pFilterList = NULL;
W3_FILTER_CONTEXT * pW3Context = NULL;
if ( _pSite != NULL )
{
//
// To avoid creating connection contexts, do the simple fast check
// to determine whether the given contexts site supports the
// notification. If it does, then we have to do the more robust
// check to determine whether this specific request requires the
// notification (there is a difference because a filter can
// disable itself on the fly for any given request)
//
pFilterList = _pSite->QueryFilterList();
DBG_ASSERT( pFilterList != NULL );
if ( pFilterList->IsNotificationNeeded( dwNotification,
QueryRequest()->IsSecureRequest() ) )
{
pW3Context = QueryFilterContext();
if ( pW3Context != NULL )
{
fNeeded = pW3Context->IsNotificationNeeded( dwNotification );
}
}
}
return fNeeded;
}
BOOL
W3_MAIN_CONTEXT::QueryExpiry(
LARGE_INTEGER * pExpiry
)
/*++
Routine Description:
Queries the expiration date/time for logon user
Arguments:
pExpiry - ptr to buffer to update with expiration date
Return Value:
TRUE if successful, FALSE if not available
--*/
{
SECURITY_STATUS ss;
SecPkgContext_PasswordExpiry speExpiry;
SSPI_SECURITY_CONTEXT * pSecurityContext;
W3_USER_CONTEXT * pW3UserContext;
LARGE_INTEGER * pUserAcctExpiry = NULL;
pUserAcctExpiry = _pUserContext->QueryExpiry();
if ( pUserAcctExpiry == NULL )
{
((LARGE_INTEGER*)pExpiry)->HighPart = 0x7fffffff;
((LARGE_INTEGER*)pExpiry)->LowPart = 0xffffffff;
return FALSE;
}
else
{
memcpy( pExpiry,
pUserAcctExpiry,
sizeof( LARGE_INTEGER ) );
}
return TRUE;
}
HRESULT
W3_MAIN_CONTEXT::ExecuteExpiredUrl(
STRU & strExpUrl
)
/*++
Routine Description:
Do child execution on the server configed expire url
Arguments:
strExpUrl - The configed expire url to be executed
Return Value:
HRESULT
--*/
{
HRESULT hr;
AUTH_PROVIDER * pAnonymousProvider = NULL;
STRA strNewHeader;
STRA strNewValue;
pAnonymousProvider = W3_STATE_AUTHENTICATION::QueryAnonymousProvider();
DBG_ASSERT( pAnonymousProvider != NULL );
hr = pAnonymousProvider->DoAuthenticate( this );
if( FAILED( hr ) )
{
return hr;
}
//
// Execute a child request
//
QueryResponse()->Clear();
QueryResponse()->SetStatus( HttpStatusOk );
//
// Reset the new url to be executed
//
hr = QueryRequest()->SetUrl( strExpUrl );
if( FAILED( hr ) )
{
return hr;
}
//
// Add CFG_ENC_CAPS header and set its value to 1 to indicate
// the site support SSL, to 0 if not.
//
strNewHeader.Copy( "CFG-ENC-CAPS" );
if( QuerySite()->QuerySSLSupported() )
{
strNewValue.Copy( "1" );
}
else
{
strNewValue.Copy( "0" );
}
hr = QueryRequest()->SetHeader( strNewHeader,
strNewValue,
TRUE );
if( FAILED( hr ) )
{
return hr;
}
//
// Set the auth access check flag to FALSE so the
// child execution won't do auth access check
//
SetAuthAccessCheckRequired( FALSE );
//
// Set finished response for parent
//
SetFinishedResponse();
//
// Execute child request
//
hr = ExecuteChildRequest( QueryRequest(),
FALSE,
W3_FLAG_ASYNC );
return hr;
}
HRESULT
W3_MAIN_CONTEXT::PasswdExpireNotify(
VOID
)
/*++
Routine Description:
Check if the user password has been expired
Arguments:
None
Return Value:
HRESULT
--*/
{
HRESULT hr = S_FALSE;
LARGE_INTEGER cExpire;
FILETIME ftNow;
DWORD dwExpireInDay;
DWORD dwTotalRequired;
STACK_STRU ( strExpUrl, MAX_PATH );
STACK_STRU ( strFullUrl, MAX_PATH );
STRU * pstrAdvNotPwdExpUrl;
BYTE byTokenInfo[ SID_DEFAULT_SIZE + sizeof( TOKEN_USER ) ];
PSID pSid;
if ( QueryExpiry( &cExpire ) )
{
if ( cExpire.HighPart == 0x7fffffff )
{
//
// Password never expire
//
return hr;
}
else
{
GetSystemTimeAsFileTime( &ftNow );
if ( *( __int64 * )&cExpire > *( __int64 * )&ftNow )
{
dwExpireInDay = ( DWORD )( ( * ( __int64 * )&cExpire
- *( __int64 * )&ftNow )
/ ( ( __int64 )10000000 * 86400 ) );
if ( QuerySite()->QueryAdvNotPwdExpInDays() &&
dwExpireInDay <= QuerySite()->QueryAdvNotPwdExpInDays() )
{
pstrAdvNotPwdExpUrl = QuerySite()->QueryAdvNotPwdExpUrl();
if( pstrAdvNotPwdExpUrl == NULL )
{
//
// Advanced password expire notification disabled
//
return hr;
}
//
// Check this SID has not already been notified
// of pwd expiration
//
if ( GetTokenInformation(
QueryUserContext()->QueryPrimaryToken(),
TokenUser,
( LPVOID )byTokenInfo,
sizeof( byTokenInfo ),
&dwTotalRequired ) )
{
pSid = ( ( TOKEN_USER * )byTokenInfo )->User.Sid;
if( !PenCheckPresentAndResetTtl(
pSid,
QuerySite()->QueryAdvCacheTTL() ) )
{
PenAddToCache(
pSid,
QuerySite()->QueryAdvCacheTTL() );
//
// flush cache when connection close
// so that account change will not be masked
// by cached information
//
if( QueryUserContext()->QueryAuthType() ==
MD_AUTH_BASIC )
{
g_pW3Server->QueryTokenCache()->FlushCacheEntry(
( ( BASIC_USER_CONTEXT * )QueryUserContext() )
->QueryCachedToken()->QueryCacheKey() );
}
hr = strExpUrl.Copy( pstrAdvNotPwdExpUrl->
QueryStr() );
if( FAILED( hr ) )
{
return hr;
}
if ( strExpUrl.QueryStr()[0] == NULL )
{
return E_FAIL;
}
//
// Add the arg to be passed to the
// password-change URL - argument is the
// URL the user is pointed to after all
// the password-change processing is done
//
hr = strExpUrl.Append( L"?" );
if( FAILED( hr ) )
{
return hr;
}
hr = QueryRequest()->GetOriginalFullUrl(
&strFullUrl );
if( FAILED( hr ) )
{
return hr;
}
hr = strExpUrl.Append( strFullUrl );
if( FAILED( hr ) )
{
return hr;
}
return ExecuteExpiredUrl( strExpUrl );
}
}
}
}
else
{
//
// flush cache when connection close
// since the password has expired
//
if( QueryUserContext()->QueryAuthType() == MD_AUTH_BASIC )
{
g_pW3Server->QueryTokenCache()->FlushCacheEntry(
( ( BASIC_USER_CONTEXT * )QueryUserContext() )
->QueryCachedToken()->QueryCacheKey() );
}
return PasswdChangeExecute();
}
}
}
return hr;
}
HRESULT
W3_MAIN_CONTEXT::PasswdChangeExecute(
VOID
)
/*++
Routine Description:
This method handles password expiration notification
Arguments:
None
Return Value:
HRESULT
--*/
{
HRESULT hr = S_FALSE;
STACK_STRU ( strExpUrl, MAX_PATH );
STACK_STRU ( strFullUrl, MAX_PATH );
STACK_STRU ( strUrl, MAX_PATH );
STRU * pstrAuthExpiredUrl;
pstrAuthExpiredUrl = QuerySite()->QueryAuthExpiredUrl();
if( pstrAuthExpiredUrl == NULL )
{
//
// S_FALSE means password change disabled
//
return hr;
}
hr = strExpUrl.Copy( pstrAuthExpiredUrl->QueryStr() );
if( FAILED( hr ) )
{
return hr;
}
if ( strExpUrl.QueryStr()[0] == NULL )
{
return E_FAIL;
}
hr = QueryRequest()->GetUrl( &strUrl );
if( FAILED( hr ) )
{
return hr;
}
//
// Add the arg to be passed to the password-change URL - argument
// is the URL the user is pointed to after all the password-change
// processing is done
//
hr = strExpUrl.Append( L"?" );
if ( FAILED( hr ) )
{
return hr;
}
hr = QueryRequest()->GetOriginalFullUrl( &strFullUrl );
if( FAILED( hr ) )
{
return hr;
}
hr = strExpUrl.Append( strFullUrl );
if( FAILED( hr ) )
{
return hr;
}
return ExecuteExpiredUrl( strExpUrl );
}
HRESULT
W3_MAIN_CONTEXT::GetRemoteDNSName(
STRA * pstrDNSName
)
/*++
Routine Description:
Get remote client's DNS name if it is resolved
Arguments:
pstrDNSName - Filled with DNS name on success
Return Value:
HRESULT
--*/
{
DBG_ASSERT( pstrDNSName != NULL );
if ( _IpAddressCheck.IsDnsResolved() )
{
return pstrDNSName->Copy( _IpAddressCheck.QueryResolvedDnsName() );
}
else
{
return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
}
}
CONTEXT_STATUS
W3_STATE_START::DoWork(
W3_MAIN_CONTEXT * pMainContext,
DWORD cbCompletion,
DWORD dwCompletionStatus
)
/*++
Routine Description:
Initial start state handling
Arguments:
pMainContext - W3_MAIN_CONTEXT representing an execution of the state machine
cbCompletion - Number of bytes on completion
dwCompletionStatus - Win32 Error on completion (if any)
Return Value:
CONTEXT_STATUS_CONTINUE - if we should continue in state machine
else stop executing the machine and free up the current thread
--*/
{
W3_REQUEST * pRequest;
DWORD dwSiteId;
HRESULT hr;
W3_SITE * pSite;
BOOL fFinished = FALSE;
BOOL fNeedRawRead = FALSE;
RAW_CONNECTION * pRawConnection = NULL;
W3_FILTER_CONTEXT * pFilterContext = NULL;
//
// Get the request out of the context and the SiteId out of the request
//
pRequest = pMainContext->QueryRequest();
DBG_ASSERT( pRequest != NULL );
dwSiteId = pRequest->QuerySiteId();
//
// Check if this site already exists
//
pSite = g_pW3Server->FindSite( dwSiteId );
//
// Now we need to do some locking while adding this site
//
if ( pSite == NULL )
{
g_pW3Server->WriteLockSiteList();
//
// try again, avoid race condition
//
pSite = g_pW3Server->FindSite( dwSiteId );
if ( pSite == NULL )
{
//
// Need to create a new site!
//
pSite = new W3_SITE( dwSiteId );
if ( pSite == NULL )
{
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
else
{
hr = pSite->Initialize();
}
if ( FAILED( hr ) )
{
if ( pSite != NULL )
{
pSite->Release();
pSite = NULL;
}
pMainContext->SetErrorStatus( hr );
pMainContext->SetFinishedResponse();
}
else
{
g_pW3Server->AddSite( pSite );
}
}
g_pW3Server->WriteUnlockSiteList();
}
if ( pSite == NULL )
{
return CONTEXT_STATUS_CONTINUE;
}
//
// If we found a site, associate it so that all future consumers can
// get at site configuration settings
//
pMainContext->AssociateSite( pSite );
//
// Let the raw data fun begin.
//
// If this request has gone thru the stream filter, then we'll need
// to associate the current W3_CONNECTION with a RAW_CONNECTION. Also,
// we'll have to retrieve a filter context
//
fNeedRawRead = FILTER_LIST::QueryGlobalList()->IsNotificationNeeded(
SF_NOTIFY_READ_RAW_DATA,
FALSE );
if ( pRequest->QueryRawConnectionId() != HTTP_NULL_ID &&
fNeedRawRead )
{
//
// Raw read filters should be loaded only in old mode
//
DBG_ASSERT( g_pW3Server->QueryInBackwardCompatibilityMode() );
//
// Find a raw connection for this request
//
hr = RAW_CONNECTION::FindConnection( pRequest->QueryRawConnectionId(),
&pRawConnection );
if ( FAILED( hr ) )
{
pMainContext->SetErrorStatus( hr );
pMainContext->SetFinishedResponse();
pMainContext->SetDisconnect( TRUE );
return CONTEXT_STATUS_CONTINUE;
}
DBG_ASSERT( pRawConnection != NULL );
//
// We will need to copy over context pointers and allocated memory
// from any existing read data filters
//
pFilterContext = pMainContext->QueryFilterContext();
if ( pFilterContext == NULL )
{
pMainContext->SetErrorStatus( hr );
pMainContext->SetFinishedResponse();
pMainContext->SetDisconnect( TRUE );
return CONTEXT_STATUS_CONTINUE;
}
pRawConnection->CopyContextPointers( pFilterContext );
pRawConnection->CopyAllocatedFilterMemory( pFilterContext );
hr = pRawConnection->CopyHeaders( pFilterContext );
if ( FAILED( hr ) )
{
pMainContext->SetErrorStatus( hr );
pMainContext->SetFinishedResponse();
pMainContext->SetDisconnect( TRUE );
return CONTEXT_STATUS_CONTINUE;
}
//
// Associate the raw connection with the main context
//
pRawConnection->SetMainContext( pMainContext );
pMainContext->SetRawConnection( pRawConnection );
}
//
// We can notify filters now that we have a site associated
//
if ( pMainContext->IsNotificationNeeded( SF_NOTIFY_PREPROC_HEADERS ) )
{
pMainContext->NotifyFilters( SF_NOTIFY_PREPROC_HEADERS,
NULL,
&fFinished );
if ( fFinished )
{
pMainContext->SetFinishedResponse();
}
}
//
// Determine the amount of bytes available to be read thru UL
//
pMainContext->DetermineRemainingEntity();
//
// Now that filters have been notified, we can increment the appropriate
// verb perf counter.
//
pSite->IncReqType( pRequest->QueryVerbType() );
return CONTEXT_STATUS_CONTINUE;
}
VOID *
W3_MAIN_CONTEXT_STATE::operator new(
size_t uiSize,
VOID * pPlacement
)
{
W3_MAIN_CONTEXT * pContext;
W3_MAIN_CONTEXT_STATE * pState;
PVOID pBuffer;
pContext = (W3_MAIN_CONTEXT*) pPlacement;
DBG_ASSERT( pContext != NULL );
DBG_ASSERT( pContext->CheckSignature() );
pBuffer = pContext->ContextAlloc( (UINT)uiSize );
DBG_ASSERT( pBuffer != NULL );
return pBuffer;
}
VOID
W3_MAIN_CONTEXT_STATE::operator delete(
VOID * pContext
)
{
//
// Do nothing here. Either
// a) memory was allocated from inline W3_MAIN_CONTEXT buffer and thus should
// not be freeed
// b) memory was allocated from heap because inline buffer didn't have
// enough space. In this case, the memory is freed on MAIN_CONTEXT
// cleanup
//
}
VOID
W3_MAIN_CONTEXT::DetermineRemainingEntity(
VOID
)
/*++
Routine Description:
Determine remaining entity body to be read from UL
Arguments:
None
Return Value:
None
--*/
{
CHAR * pszContentLength;
DWORD cbContentLength;
if ( _request.QueryMoreEntityBodyExists() )
{
pszContentLength = _request.GetHeader( HttpHeaderContentLength );
if ( pszContentLength != NULL )
{
cbContentLength = atoi( pszContentLength );
if ( _request.QueryAvailableBytes() <= cbContentLength )
{
_cbRemainingEntityFromUL = cbContentLength - _request.QueryAvailableBytes();
}
else
{
_cbRemainingEntityFromUL = 0;
}
}
else
{
_cbRemainingEntityFromUL = INFINITE;
}
}
else
{
_cbRemainingEntityFromUL = 0;
}
}
HRESULT
W3_MAIN_CONTEXT::SetupCertificateContext(
HTTP_SSL_CLIENT_CERT_INFO * pClientCertInfo
)
/*++
Routine Description:
Create a CERTIFICATE_CONTEXT representing the given client certificate
Arguments:
pClientCertInfo - Client cert info from stream filter
Return Value:
HRESULT
--*/
{
if ( pClientCertInfo == NULL )
{
DBG_ASSERT( FALSE );
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
}
//
// Just for completeness sake, attach the raw cert descriptor to the
// main request, as it automatically be for subsequent requests on this
// connection
//
QueryRequest()->SetClientCertInfo( pClientCertInfo );
//
// Create a client certificate descriptor and associate it with
//
DBG_ASSERT( _pCertificateContext == NULL );
_pCertificateContext = new CERTIFICATE_CONTEXT( pClientCertInfo );
if ( _pCertificateContext == NULL )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
return NO_ERROR;
}
VOID
W3_MAIN_CONTEXT::SetRawConnection(
RAW_CONNECTION * pRawConnection
)
/*++
Routine Description:
Set a raw connection for this context. This raw connection is stored so
that we can disassociate ourselves with it when the state machine is
complete
Arguments:
pRawConnection - Raw connection
Return Value:
None
--*/
{
pRawConnection->ReferenceRawConnection();
_pRawConnection = pRawConnection;
}
CONTEXT_STATUS
W3_STATE_DONE::DoWork(
W3_MAIN_CONTEXT * pMainContext,
DWORD cbCompletion,
DWORD dwCompletionStatus
)
/*++
Routine Description:
Do the done state stuff
Arguments:
pMainContext - W3_MAIN_CONTEXT representing an execution of the state machine
cbCompletion - Number of bytes on completion
dwCompletionStatus - Win32 Error on completion (if any)
Return Value:
CONTEXT_STATUS_CONTINUE - if we should continue in state machine
else stop executing the machine and free up the current thread
--*/
{
return CONTEXT_STATUS_CONTINUE;
}
CONTEXT_STATUS
W3_STATE_DONE::OnCompletion(
W3_MAIN_CONTEXT * pMainContext,
DWORD cbCompletion,
DWORD dwCompletionStatus
)
/*++
Routine Description:
Complete the done state
Arguments:
pMainContext - W3_MAIN_CONTEXT representing an execution of the state machine
cbCompletion - Number of bytes on completion
dwCompletionStatus - Win32 Error on completion (if any)
Return Value:
CONTEXT_STATUS_CONTINUE - if we should continue in state machine
else stop executing the machine and free up the current thread
--*/
{
DBG_ASSERT( pMainContext != NULL );
//
// We could only get to here, if a send raw notification caused us to
// pend the done state until the connection goes away, or the current
// context is disassociated with the connection
//
DBG_ASSERT( pMainContext->IsNotificationNeeded( SF_NOTIFY_SEND_RAW_DATA ) );
return CONTEXT_STATUS_CONTINUE;
}
//static
VOID
W3_MAIN_CONTEXT::OnNewRequest(
ULATQ_CONTEXT ulatqContext
)
/*++
Routine Description:
Completion routine called when a new request is dequeued to be handled
Arguments:
ulatqContext - ULATQ_CONTEXT representing the request
Return Value:
None
--*/
{
HTTP_REQUEST * pUlHttpRequest = NULL;
W3_CONNECTION * pConnection = NULL;
W3_MAIN_CONTEXT * pMainContext = NULL;
HRESULT hr = NO_ERROR;
InterlockedIncrement( &sm_cOutstandingThreads );
//
// Get the HTTP_REQUEST for this new request
//
pUlHttpRequest = (HTTP_REQUEST *)UlAtqGetContextProperty(
ulatqContext,
ULATQ_PROPERTY_HTTP_REQUEST );
DBG_ASSERT( pUlHttpRequest != NULL );
//
// Setup the MAIN_CONTEXT for this new request
//
pMainContext = new W3_MAIN_CONTEXT( pUlHttpRequest,
ulatqContext );
if (NULL == pMainContext)
{
UlAtqFreeContext(ulatqContext);
goto done;
}
pMainContext->_LogContext.m_dwBytesRecvd = pUlHttpRequest->BytesReceived;
//
// Start the state machine
//
pMainContext->DoWork( 0,
0,
FALSE );
done:
InterlockedDecrement( &sm_cOutstandingThreads );
}
//static
VOID
W3_MAIN_CONTEXT::OnPostedCompletion(
DWORD dwCompletionStatus,
DWORD cbTransferred,
LPOVERLAPPED lpo
)
/*++
Routine Description:
Fake completion routine called when we want to fake a completion for
asynchronous sanity sake
Arguments:
dwCompletionStatus - Error (if any) on the completion
cbTransferred - Bytes Written
lpo - Overlapped
Return Value:
None
--*/
{
W3_MAIN_CONTEXT * pMainContext;
InterlockedIncrement( &sm_cOutstandingThreads );
pMainContext = (W3_MAIN_CONTEXT *)lpo;
DBG_ASSERT( pMainContext != NULL );
//
// Continue the state machine
//
pMainContext->DoWork( cbTransferred,
dwCompletionStatus,
TRUE );
InterlockedDecrement( &sm_cOutstandingThreads );
}
//static
VOID
W3_MAIN_CONTEXT::OnIoCompletion(
PVOID pvContext,
DWORD cbTransferred,
DWORD dwCompletionStatus,
OVERLAPPED * lpo
)
/*++
Routine Description:
Completion routine called on async IO completions for a given request
Arguments:
pvContext - Completion context (a UL_REQUEST*)
cbTransferred - Bytes on the completion
dwCompletionStatus - Error (if any) on the completion
lpo - Overlapped
Return Value:
None
--*/
{
W3_MAIN_CONTEXT * pMainContext;
InterlockedIncrement( &sm_cOutstandingThreads );
pMainContext = (W3_MAIN_CONTEXT*) pvContext;
DBG_ASSERT( pMainContext != NULL );
DBG_ASSERT( pMainContext->CheckSignature() );
//
// Continue the state machine
//
pMainContext->DoWork( cbTransferred,
dwCompletionStatus,
TRUE );
InterlockedDecrement( &sm_cOutstandingThreads );
}
//static
VOID
W3_MAIN_CONTEXT::AddressResolutionCallback(
ADDRCHECKARG pContext,
BOOL fUnused,
LPSTR pszUnused
)
/*++
Routine Description:
Callback called when RDNS crud has done its resolution
Arguments:
pContext - Context (in our case, pointer to W3_MAIN_CONTEXT so that
we can resume state machine)
fUnused - Not used
pszUnused - Not used
Return Value:
None
--*/
{
W3_MAIN_CONTEXT * pMainContext;
pMainContext = (W3_MAIN_CONTEXT*) pContext;
DBG_ASSERT( pMainContext != NULL );
DBG_ASSERT( pMainContext->CheckSignature() );
ThreadPoolPostCompletion( 0,
W3_MAIN_CONTEXT::OnPostedCompletion,
(OVERLAPPED*) pMainContext );
}
VOID
W3_MAIN_CONTEXT::TimerCallback(LPVOID pvParam,
BOOLEAN fReason)
/*++
Routine Description:
Callback called when W3_MAIN_CONTEXT has existed for too long
OutputDebugString to tell people why we are doing this
and DebugBreak if a debugger is attached.
If no debugger is attached, ignore the callback.
Arguments:
pvParam - pointer to W3_MAIN_CONTEXT that has exceeded its maximum lifetime
fReason - not used
Return Value:
void
--*/
{
W3_MAIN_CONTEXT* pThis = (W3_MAIN_CONTEXT*)pvParam;
if (IsDebuggerPresent())
{
OutputDebugString(L"****************\nIIS (w3core.dll) has called DebugBreak because\nHKLM\\System\\CurrentControlSet\\Services\\InetInfo\\Parameters\\RequestTimeoutBreak\nwas set.\nAnd a request has taken longer than the specified maximium time in milliseconds\n****************\n");
DebugBreak();
}
return;
}