Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2956 lines
70 KiB

/*++
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;
DWORD W3_MAIN_CONTEXT::sm_dwTimeout = 0;
HANDLE W3_MAIN_CONTEXT::sm_hTraceFile = INVALID_HANDLE_VALUE;
W3_TRACE_LOG_FACTORY * W3_MAIN_CONTEXT::sm_pLogFactory = NULL;
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)
{
IncrementBytesRecvd( 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::UpdateSkipped(
HTTP_DATA_CHUNK * pChunks,
DWORD cChunks
)
/*++
Routine Description:
Update the amount of worker process data to skip
Arguments:
pChunks - Array of chunks
cChunks - Number of chunks
Return Value:
None
--*/
{
ULARGE_INTEGER liTotalData;
ULARGE_INTEGER liFileSize;
ULARGE_INTEGER liChunkSize;
HTTP_DATA_CHUNK * pDataChunk;
HTTP_BYTE_RANGE * pByteRange;
BOOL fRet;
DBG_ASSERT( (pChunks != NULL) || (cChunks == 0) );
if ( _pRawConnection == NULL )
{
return;
}
liTotalData.QuadPart = 0;
for ( int i = 0; i < cChunks; i++ )
{
pDataChunk = &( pChunks[ i ] );
if ( pDataChunk->DataChunkType == HttpDataChunkFromMemory )
{
liTotalData.QuadPart += pDataChunk->FromMemory.BufferLength;
}
else if ( pDataChunk->DataChunkType == HttpDataChunkFromFileHandle )
{
pByteRange = &( pDataChunk->FromFileHandle.ByteRange );
if ( pByteRange->StartingOffset.QuadPart == HTTP_BYTE_RANGE_TO_EOF )
{
liTotalData.QuadPart += pByteRange->Length.QuadPart;
}
else if ( pByteRange->Length.QuadPart == HTTP_BYTE_RANGE_TO_EOF )
{
fRet = GetFileSizeEx( pDataChunk->FromFileHandle.FileHandle,
(PLARGE_INTEGER) &liFileSize );
if ( fRet )
{
liChunkSize.QuadPart = liFileSize.QuadPart - pByteRange->StartingOffset.QuadPart;
liTotalData.QuadPart += liChunkSize.QuadPart;
}
}
else
{
liTotalData.QuadPart += pByteRange->Length.QuadPart;
}
}
}
_pRawConnection->AddSkippedData( liTotalData );
}
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;
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;
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;
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;
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;
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;
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;
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;
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;
cState++;
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,
0 /* Recursion Level */),
_pSite ( NULL ),
_pFilterContext ( NULL ),
_fDisconnect ( FALSE ),
_fNeedFinalDone ( FALSE ),
_fAssociationChecked ( FALSE ),
_pConnection ( NULL ),
_pUrlContext ( NULL ),
_pUserContext ( NULL ),
_fProviderHandled ( FALSE ),
_fCheckAnonAuthTypeSupported ( FALSE ),
_fDoneWithCompression ( FALSE ),
_pCompressionContext ( NULL ),
_fIsUlCacheable ( TRUE ),
_pCertificateContext ( NULL ),
_cbRemainingEntityFromUL ( 0 ),
_cbEntityReadSoFar ( 0 ),
_fGenerateContentLength ( FALSE ),
_pRawConnection ( NULL ),
_cRefs ( 1 ),
_hTimer ( NULL ),
_dwLastPostLineNumber ( 0 ),
_pszLastPostFileName ( NULL ),
_pTraceLog ( NULL ),
_fIgnoreAppPoolCheck ( FALSE )
{
_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);
}
if ( sm_pLogFactory )
{
sm_pLogFactory->CreateTraceLog( &_pTraceLog );
}
}
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 );
//
// Only dereference if this is a detached context
//
if ( _pFilterContext != &_FilterContext )
{
_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;
}
//
// Finally release the site
//
if ( _pSite != NULL )
{
_pSite->DereferenceSite();
_pSite = NULL;
}
if (_hTimer != NULL )
{
BOOL fRet;
fRet = DeleteTimerQueueTimer(NULL,
_hTimer,
INVALID_HANDLE_VALUE);
DBG_ASSERT(fRet);
_hTimer = NULL;
}
if ( _pTraceLog != NULL )
{
_pTraceLog->DestroyTraceLog();
_pTraceLog = 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;
// ignore the return value- if we can't setup trace logging, we can't setup trace logging
SetupTraceLogging();
//
// 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 );
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::Terminate(
VOID
)
/*++
Routine Description:
Terminate MAIN_CONTEXT globals
Arguments:
None
Return Value:
None
--*/
{
CleanupStateMachine();
if ( sm_pachMainContexts != NULL )
{
delete sm_pachMainContexts;
sm_pachMainContexts = NULL;
}
CleanupTraceLogging();
}
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;
}
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
(default TRUE)
Return Value:
Pointer to a new W3_FILTER_CONTEXT
--*/
{
BOOL fSecure;
BOOL fCreateNew = FALSE;
if ( _pFilterContext == NULL &&
fCreateIfNotFound )
{
//
// OK. Now it gets interesting.
//
// Every W3_MAIN_CONTEXT has inline with it a W3_FILTER_CONTEXT.
// This is sufficient for all cases except for filters which
// require an END_OF_NET_SESSION notification. In this case
// the W3_FILTER_CONTEXT must have a lifetime beyond the
// W3_MAIN_CONTEXT. When creating a W3_FILTER_CONTEXT here,
// we check whether an END_OF_NET_SESSION notification is needed
// If so we create a new filter context to associate with this
// request
//
DBG_ASSERT( _pSite != NULL );
fSecure = QueryRequest()->IsSecureRequest();
if ( _pSite->QueryFilterList() != NULL )
{
if ( _pSite->QueryFilterList()->IsNotificationNeeded( SF_NOTIFY_END_OF_NET_SESSION,
fSecure ) )
{
fCreateNew = TRUE;
}
}
if ( fCreateNew )
{
_pFilterContext = new W3_FILTER_CONTEXT;
}
else
{
_pFilterContext = &_FilterContext;
}
if ( _pFilterContext != NULL )
{
_pFilterContext->InitializeFilterContext( fSecure,
_pSite->QueryFilterList(),
fCreateNew ? FALSE : TRUE );
_pFilterContext->SetMainContext( this );
}
}
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 * pFilterContext = 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() ) )
{
pFilterContext = QueryFilterContext();
if ( pFilterContext != NULL )
{
fNeeded = pFilterContext->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;
BOOL fFilterFinished = FALSE;
W3_USER_CONTEXT * pUserContext = QueryUserContext();
if( pUserContext != NULL )
{
SetConnectionUserContext( NULL );
pUserContext->DereferenceUserContext();
pUserContext = NULL;
}
pAnonymousProvider = W3_STATE_AUTHENTICATION::QueryAnonymousProvider();
DBG_ASSERT( pAnonymousProvider != NULL );
hr = pAnonymousProvider->DoAuthenticate( this,
&fFilterFinished );
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 );
//
// 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() &&
_IpAddressCheck.QueryResolvedDnsName()[0] != '\0' )
{
return pstrDNSName->Copy( _IpAddressCheck.QueryResolvedDnsName() );
}
else
{
return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
}
}
VOID *
W3_MAIN_CONTEXT::operator new(
size_t uiSize,
VOID * pPlacement
)
/*++
Routine Description:
Allocate a new W3_MAIN_CONTEXT (from inline or ACache)
Arguments:
uiSize - Size to allocate
pPlacement - UL_NATIVE_REQUEST
Return Value:
Pointer to memory (or NULL on failure)
--*/
{
ULATQ_CONTEXT ulatqContext;
//
// If read filtering is needed, then W3_MAIN_CONTEXTs have a lifetime
// beyond their UL_NATIVE_REQUEST lifetime. Therefore use
// the regular ACache
//
if ( RAW_CONNECTION::QueryDoReadRawFiltering() )
{
return sm_pachMainContexts->Alloc();
}
else
{
ulatqContext = (ULATQ_CONTEXT) pPlacement;
return UlAtqAllocateMemory( ulatqContext, uiSize );
}
}
VOID
W3_MAIN_CONTEXT::operator delete(
VOID * pMainContext
)
/*++
Routine Description:
Delete a W3_MAIN_CONTEXT
Arguments:
pMainContext - context to delete
Return Value:
None
--*/
{
if ( RAW_CONNECTION::QueryDoReadRawFiltering() )
{
sm_pachMainContexts->Free( pMainContext );
}
}
VOID *
W3_MAIN_CONTEXT::AllocateFromInlineMemory(
DWORD cbSize
)
/*++
Routine Description:
Allocate from UL_NATIVE_REQUEST structure
Arguments:
cbSize - size to allocate
Return Value:
Pointer to memory (or NULL if failure)
--*/
{
if ( RAW_CONNECTION::QueryDoReadRawFiltering() )
{
return NULL;
}
else
{
return UlAtqAllocateMemory( _ulatqContext, cbSize );
}
}
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;
BOOL fNeedRealRawWrite = FALSE;
RAW_CONNECTION * pRawConnection = NULL;
W3_FILTER_CONTEXT * pFilterContext = NULL;
W3_TRACE_LOG * pTraceLog = pMainContext->QueryTraceLog();
//
// Get the request out of the context and the SiteId out of the request
//
pRequest = pMainContext->QueryRequest();
DBG_ASSERT( pRequest != NULL );
if ( pTraceLog != NULL )
{
pTraceLog->Trace( L"%I64x: Received new request\n",
pRequest->QueryRequestId() );
}
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->DereferenceSite();
pSite = NULL;
}
pMainContext->SetErrorStatus( hr );
pMainContext->SetFinishedResponse();
}
else
{
g_pW3Server->AddSite( pSite );
}
}
g_pW3Server->WriteUnlockSiteList();
}
if ( pSite == NULL )
{
if ( pTraceLog != NULL )
{
pTraceLog->Trace( L"%I64x: Failed to find site %d\n",
pRequest->QueryRequestId(),
dwSiteId );
}
return CONTEXT_STATUS_CONTINUE;
}
if ( pTraceLog != NULL )
{
pTraceLog->Trace( L"%I64x: Successfully associated site %d\n",
pRequest->QueryRequestId(),
dwSiteId );
}
//
// 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,
pRequest->IsSecureRequest() );
//
// If we are in backward compat mode, AND send raw notifications are
// needed, then we will use streamfilt to get the data which WPs do
// not generate
//
if ( g_pW3Server->QueryInBackwardCompatibilityMode() )
{
fNeedRealRawWrite = FILTER_LIST::QueryGlobalList()->IsNotificationNeeded(
SF_NOTIFY_SEND_RAW_DATA,
pRequest->IsSecureRequest() );
if ( fNeedRealRawWrite )
{
pMainContext->SetNeedFinalDone();
}
}
if ( pRequest->QueryRawConnectionId() != HTTP_NULL_ID &&
( fNeedRawRead || fNeedRealRawWrite ) )
{
//
// 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 assume we don't have to skip bytes until a send raw filter
// causes us to
//
if ( fNeedRealRawWrite )
{
pRawConnection->EnableSkip();
}
//
// 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 ) )
{
if ( !pMainContext->NotifyFilters( SF_NOTIFY_PREPROC_HEADERS,
NULL,
&fFinished ) )
{
DWORD dwError = GetLastError();
pMainContext->SetErrorStatus( HRESULT_FROM_WIN32( dwError ) );
if ( dwError == ERROR_ACCESS_DENIED )
{
URL_CONTEXT * pUrlContext;
pMainContext->QueryResponse()->SetStatus( HttpStatusUnauthorized,
Http401Filter );
//
// In order for the ACCESS_DENIED notification to fire,
// and the correct 401 customer error to get returned,
// we need to associate a URL_CONTEXT with this request.
//
// It's not necessary to handle the failure case, since
// a failure will still result in a "default" 401
// response.
//
URL_CONTEXT::RetrieveUrlContext( pMainContext,
pMainContext->QueryRequest(),
&pUrlContext,
&fFinished,
TRUE );
}
else if ( dwError == ERROR_FILE_NOT_FOUND ||
dwError == ERROR_PATH_NOT_FOUND )
{
pMainContext->QueryResponse()->SetStatus( HttpStatusNotFound );
}
else
{
pMainContext->QueryResponse()->SetStatus( HttpStatusServerError );
}
pMainContext->SetFinishedResponse();
pMainContext->SetDisconnect( TRUE );
return CONTEXT_STATUS_CONTINUE;
}
if ( fFinished )
{
pMainContext->SetFinishedResponse();
}
}
//
// If we need an END_OF_NET_SESSION notification, then get a connect
//
if ( pMainContext->IsNotificationNeeded( SF_NOTIFY_END_OF_NET_SESSION ) )
{
pFilterContext = pMainContext->QueryFilterContext();
DBG_ASSERT( pFilterContext != NULL );
pFilterContext->QueryConnectionContext( TRUE );
}
//
// Determine the amount of bytes available to be read thru UL
//
pMainContext->DetermineRemainingEntity();
pMainContext->SetInitialEntityReadSize( pRequest->QueryAvailableBytes() );
//
// 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
)
/*++
Routine Description:
Allocate from the inline buffer contained in the main context
Arguments:
uiSize - Size to allocate
pPlacement - Points to a W3_CONTEXT to allocate from
Return Value:
None
--*/
{
W3_CONTEXT * pContext;
PVOID pBuffer;
pContext = (W3_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
--*/
{
LPCSTR 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 = pRawConnection;
}
VOID
W3_MAIN_CONTEXT::SetRawConnectionClientContext(
DWORD dwCurrentFilter,
VOID * pvContext
)
/*++
Routine Description:
Sets a filter's client context in the raw connection for this
context, if it exists. This is necessary so that an
SF_NOTIFY_READ_RAW_DATA event for a new request over this
connection will see the client context set by a previous
request.
Arguments:
dwCurrentFilter - The current filter
pvContext - The client's context
Return Value:
None
--*/
{
if ( _pRawConnection )
{
_pRawConnection->SetLocalClientContext( dwCurrentFilter,
pvContext );
}
}
BOOL
W3_MAIN_CONTEXT::QueryRawConnectionNotificationChanged(
VOID
)
{
BOOL fRet = FALSE;
if ( _pRawConnection )
{
fRet = _pRawConnection->QueryRawConnectionNotificationChanged();
}
return fRet;
}
BOOL
W3_MAIN_CONTEXT::IsRawConnectionDisableNotificationNeeded(
DWORD dwFilter,
DWORD dwNotification
)
{
BOOL fRet = FALSE;
if ( _pRawConnection )
{
fRet = _pRawConnection->IsRawConnectionDisableNotificationNeeded( dwFilter,
dwNotification );
}
return fRet;
}
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
--*/
{
W3_TRACE_LOG * pTraceLog = pMainContext->QueryTraceLog();
W3_REQUEST *pRequest = pMainContext->QueryRequest();
HTTP_REQUEST_ID RequestId = pRequest->QueryRequestId();
DWORD cbSent = pMainContext->QueryLogContext()->m_dwBytesSent;
if ( pTraceLog != NULL )
{
pTraceLog->Trace( L"%I64x: Finished request\n",
RequestId );
}
if ( ETW_IS_TRACE_ON(ETW_LEVEL_CP) )
{
//
// Trace END of request
//
g_pEtwTracer->EtwTraceEvent(&IISEventGuid, ETW_TYPE_END,
&RequestId, sizeof(HTTP_REQUEST_ID),
&cbSent, sizeof(DWORD),
NULL, 0);
}
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;
//
// 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 (ulatqContext) W3_MAIN_CONTEXT( pUlHttpRequest,
ulatqContext );
if (NULL == pMainContext)
{
UlAtqFreeContext( ulatqContext );
return;
}
pMainContext->_LogContext.m_dwBytesRecvd = pUlHttpRequest->BytesReceived;
if ( ETW_IS_TRACE_ON(ETW_LEVEL_CP) )
{
g_pEtwTracer->EtwTraceEvent( &IISEventGuid,
ETW_TYPE_START,
&pUlHttpRequest->RequestId,
sizeof( HTTP_REQUEST_ID ),
NULL,
0 );
}
//
// Start the state machine
//
pMainContext->DoWork( 0,
0,
FALSE );
}
//static
VOID
W3_MAIN_CONTEXT::PostMainCompletion(
CHAR * pszFileName,
DWORD dwLineNumber,
W3_MAIN_CONTEXT * pMainContext
)
/*++
Routine Description:
Post to the thread pool to continue the state machine. Before doing so,
record the filename and linenumber of the poster so that it makes it
easier to identify who posted, in the case of state machine breaks
(heaven forbid)
Arguments:
pszFileName - Name of source file doing the post
dwLineNumber - Line number of the post
pMainContext - Main context to post
Return Value:
None
--*/
{
BOOL fRet;
DBG_ASSERT( pszFileName != NULL );
DBG_ASSERT( dwLineNumber != 0 );
DBG_ASSERT( pMainContext != NULL );
DBG_ASSERT( pMainContext->CheckSignature() );
pMainContext->_pszLastPostFileName = pszFileName;
pMainContext->_dwLastPostLineNumber = dwLineNumber;
fRet = ThreadPoolPostCompletion( 0,
W3_MAIN_CONTEXT::OnPostedCompletion,
(OVERLAPPED*) pMainContext );
DBG_ASSERT( fRet );
}
//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;
pMainContext = (W3_MAIN_CONTEXT *)lpo;
DBG_ASSERT( pMainContext != NULL );
//
// Continue the state machine
//
pMainContext->DoWork( cbTransferred,
dwCompletionStatus,
TRUE );
}
//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;
HRESULT hr;
pMainContext = (W3_MAIN_CONTEXT*) pvContext;
DBG_ASSERT( pMainContext != NULL );
DBG_ASSERT( pMainContext->CheckSignature() );
//
// First give the response a chance to handle the completion
//
hr = pMainContext->QueryResponse()->OnIoCompletion(
pMainContext->QueryCurrentContext(),
cbTransferred,
dwCompletionStatus );
if ( hr == NO_ERROR )
{
return;
}
//
// A failure from W3_RESPONSE__OnIoCompletion means the response wanted
// nothing to do with this completion, so we can funnel it to the
// state machine
//
//
// Continue the state machine
//
pMainContext->DoWork( cbTransferred,
dwCompletionStatus,
TRUE );
}
//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() );
POST_MAIN_COMPLETION( 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;
}
BOOL
W3_MAIN_CONTEXT::QueryDisconnect()
{
if (QueryUrlContext() == NULL ||
QueryUrlContext()->QueryMetaData() == NULL ||
QueryUrlContext()->QueryMetaData()->QueryKeepAliveEnabled())
{
return _fDisconnect;
}
return TRUE;
}
//static
HRESULT
W3_MAIN_CONTEXT::SetupTraceLogging()
{
HRESULT hr = S_OK;
HKEY hKey = NULL;
LONG lRet = 0;
STACK_STRU(struFileName, MAX_PATH);
DWORD cbSize = MAX_PATH * sizeof( WCHAR );
DWORD dwType;
WCHAR buffer[sizeof("0123456789")] = {0};
lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
REGISTRY_KEY_W3SVC_PARAMETERS_W,
0,
KEY_READ,
&hKey); //RegOpenKeyEx
if ( ERROR_SUCCESS != lRet )
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto exit;
}
lRet = RegQueryValueEx(hKey,
L"TraceFile",
NULL,
&dwType,
(LPBYTE)struFileName.QueryStr(),
&cbSize); //RegQueryValueEx
if (ERROR_SUCCESS != lRet ||
REG_SZ != dwType )
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto exit;
}
struFileName.QueryStr()[ cbSize / sizeof( WCHAR ) - 1 ] = L'\0';
struFileName.SyncWithBuffer();
wsprintf( buffer, L"%lu", GetCurrentProcessId() );
hr = struFileName.Append(buffer);
if (FAILED(hr))
{
goto exit;
}
sm_hTraceFile = CreateFile(struFileName.QueryStr(),
GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL); // CreateFile
if (INVALID_HANDLE_VALUE == sm_hTraceFile)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto exit;
}
//
// Create TraceLog factory
//
hr = W3_TRACE_LOG_FACTORY::CreateTraceLogFactory(&sm_pLogFactory, sm_hTraceFile);
if (FAILED(hr))
{
goto exit;
}
exit:
if (FAILED(hr))
{
CleanupTraceLogging();
}
if (hKey)
{
RegCloseKey(hKey);
hKey = NULL;
}
return hr;
}
//static
VOID
W3_MAIN_CONTEXT::CleanupTraceLogging()
{
if ( sm_pLogFactory != NULL )
{
sm_pLogFactory->DestroyTraceLogFactory();
sm_pLogFactory = NULL;
}
if ( sm_hTraceFile != INVALID_HANDLE_VALUE )
{
CloseHandle(sm_hTraceFile);
sm_hTraceFile = INVALID_HANDLE_VALUE;
}
return;
}