|
|
/*++
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; }
|