2460 lines
55 KiB
C++
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;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|