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.
 
 
 
 
 
 

3654 lines
90 KiB

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name :
ulcontext.cxx
Abstract:
Implementation of FILTER_CHANNEL_CONTEXT. One such object for every connection
Environment:
Win32 - User Mode
Project:
Stream Filter Worker Process
--*/
#include "precomp.hxx"
#ifndef MAXDWORD
#define MAXDWORD 0xFFFFFFFFul
#endif
//static
ALLOC_CACHE_HANDLER *
SSL_SERVER_FILTER_CHANNEL_CONTEXT::sm_pachFilterChannelContexts = NULL;
//static
ALLOC_CACHE_HANDLER *
UL_OVERLAPPED_CONTEXT::sm_pachUlOverlappedContexts = NULL;
//static
ALLOC_CACHE_HANDLER *
UL_OVERLAPPED_CONTEXT::sm_pachRawWriteBuffers = NULL;
//static
ALLOC_CACHE_HANDLER *
UL_OVERLAPPED_CONTEXT::sm_pachRawReadBuffers = NULL;
//static
ALLOC_CACHE_HANDLER *
UL_OVERLAPPED_CONTEXT::sm_pachAppReadBuffers = NULL;
//static
DWORD
FILTER_CHANNEL::sm_dwDefaultRawReadSize = DEFAULT_RAW_READ_SIZE ;
//static
DWORD
FILTER_CHANNEL::sm_dwDefaultAppReadSize = DEFAULT_APP_READ_SIZE;
//static
DWORD
FILTER_CHANNEL::sm_dwContextDesiredOutstanding = UL_CONTEXT_DESIRED_OUTSTANDING;
//static
BOOL
FILTER_CHANNEL::sm_fEnableTemporaryBuffers = TRUE;
//static
DWORD
FILTER_CHANNEL::sm_dwHandshakeTimeoutInSec = DEFAULT_RENEGOTIATION_TIMEOUT_IN_SEC;
UL_OVERLAPPED_CONTEXT::UL_OVERLAPPED_CONTEXT(
UL_OVERLAPPED_CONTEXT_TYPE type,
UL_OVERLAPPED_CONTEXT_SUBTYPE subtype
)
{
_type = type;
_subtype = subtype;
_pCallback = NULL;
_pCallbackParam = NULL;
ZeroMemory( &_Overlapped, sizeof(_Overlapped) );
// internal Data buffer related initialization
_pbData = NULL;
_cbData = 0;
_fDynAllocated = FALSE;
switch ( QueryType() )
{
case UL_OVERLAPPED_CONTEXT_RAW_READ:
_pCurrentACache = sm_pachRawReadBuffers;
_cbCurrentACacheElementSize =
FILTER_CHANNEL::QueryDefaultRawReadSize();
break;
case UL_OVERLAPPED_CONTEXT_RAW_WRITE:
_pCurrentACache = sm_pachRawWriteBuffers;
_cbCurrentACacheElementSize =
FILTER_CHANNEL::QueryDefaultRawWriteSize();
break;
case UL_OVERLAPPED_CONTEXT_APP_READ:
_pCurrentACache = sm_pachAppReadBuffers;
_cbCurrentACacheElementSize =
FILTER_CHANNEL::QueryDefaultAppReadSize();
break;
case UL_OVERLAPPED_CONTEXT_APP_WRITE:
default:
_pCurrentACache = NULL;
_cbCurrentACacheElementSize = 0;
break;
}
}
//static
HRESULT
UL_OVERLAPPED_CONTEXT::Initialize(
VOID
)
/*++
Routine Description:
Global initialization routine for UL_OVERLAPPED_CONTEXT
Arguments:
None
Return Value:
HRESULT
--*/
{
ALLOC_CACHE_CONFIGURATION acConfig;
//
// Setup allocation lookaside
//
acConfig.nConcurrency = 1;
acConfig.nThreshold = 100;
// UL_OVERLAPPED_CONTEXT ACACHE
acConfig.cbSize = sizeof( UL_OVERLAPPED_CONTEXT );
DBG_ASSERT( sm_pachUlOverlappedContexts == NULL );
sm_pachUlOverlappedContexts = new ALLOC_CACHE_HANDLER( "UL_OVERLAPPED_CONTEXT",
&acConfig );
if ( sm_pachUlOverlappedContexts == NULL )
{
return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
// Raw Write Buffers ACACHE
acConfig.cbSize =
FILTER_CHANNEL::QueryDefaultRawWriteSize();
DBG_ASSERT( sm_pachRawWriteBuffers == NULL );
sm_pachRawWriteBuffers = new ALLOC_CACHE_HANDLER( "RAW_WRITE_BUFFERS",
&acConfig );
if ( sm_pachRawWriteBuffers == NULL )
{
return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
// Raw Read Buffers ACACHE
acConfig.cbSize =
FILTER_CHANNEL::QueryDefaultRawReadSize();
DBG_ASSERT( sm_pachRawReadBuffers == NULL );
sm_pachRawReadBuffers = new ALLOC_CACHE_HANDLER( "RAW_READ_BUFFERS",
&acConfig );
if ( sm_pachRawReadBuffers == NULL )
{
return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
// App Read Buffers ACACHE
acConfig.cbSize =
FILTER_CHANNEL::QueryDefaultAppReadSize();
DBG_ASSERT( sm_pachAppReadBuffers == NULL );
sm_pachAppReadBuffers = new ALLOC_CACHE_HANDLER( "APP_READ_BUFFERS",
&acConfig );
if ( sm_pachAppReadBuffers == NULL )
{
return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
return NO_ERROR;
}
//static
VOID
UL_OVERLAPPED_CONTEXT::Terminate(
VOID
)
/*++
Routine Description:
Terminate UL_OVERLAPPED_CONTEXT globals
Arguments:
None
Return Value:
None
--*/
{
if ( sm_pachAppReadBuffers != NULL )
{
delete sm_pachAppReadBuffers;
sm_pachAppReadBuffers = NULL;
}
if ( sm_pachRawReadBuffers != NULL )
{
delete sm_pachRawReadBuffers;
sm_pachRawReadBuffers = NULL;
}
if ( sm_pachRawWriteBuffers != NULL )
{
delete sm_pachRawWriteBuffers;
sm_pachRawWriteBuffers = NULL;
}
if ( sm_pachUlOverlappedContexts != NULL )
{
delete sm_pachUlOverlappedContexts;
sm_pachUlOverlappedContexts = NULL;
}
}
BOOL
UL_OVERLAPPED_CONTEXT::ResizeDataBuffer(
DWORD cbNewSize
)
/*++
Routine Description:
Resize internal data buffer
- if requested size fits within the range of the
ACACHE entry size then ACACHEd entry is returned
Otherwise heap allocation is made.
Content of the previous buffer is copied to the new one
Arguments:
None
Return Value:
BOOL (this is just to follow the legacy of the BUFFER type that
was used previously)
--*/
{
if ( _cbData >= cbNewSize )
{
return TRUE;
}
PBYTE pbPrevData = _pbData;
DWORD cbPrevData = _cbData;
BOOL fPrevDynAllocated = _fDynAllocated;
if ( _cbCurrentACacheElementSize >= cbNewSize &&
_pCurrentACache != NULL )
{
//
// use ACACHE
//
_pbData = (PBYTE) _pCurrentACache->Alloc();
if ( _pbData != NULL )
{
_cbData = _cbCurrentACacheElementSize;
_fDynAllocated = FALSE;
}
}
else
{
//
// Requested size was too big for ACACHE
// We have to allocate on the heap
// round up the allocation unit to minimize access to the heap
//
DWORD cbAllocIncrement = 4096;
DWORD cbNewAllocSize = cbAllocIncrement *
( ( cbNewSize / cbAllocIncrement ) + 1 );
_pbData = new BYTE[ cbNewAllocSize ];
if (_pbData != NULL )
{
_cbData = cbNewAllocSize;
_fDynAllocated = TRUE;
}
}
if ( _pbData == NULL )
{
_pbData = pbPrevData;
_cbData = cbPrevData;
_fDynAllocated = fPrevDynAllocated;
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
return FALSE;
}
//
// Copy previous content if necessary
//
if ( cbPrevData != 0 && pbPrevData != NULL )
{
memcpy( _pbData, pbPrevData, cbPrevData );
}
if ( pbPrevData != NULL )
{
if ( fPrevDynAllocated )
{
delete [] pbPrevData;
}
else
{
_pCurrentACache->Free( pbPrevData );
}
}
return TRUE;
}
VOID
UL_OVERLAPPED_CONTEXT::FreeDataBuffer(
VOID
)
/*++
Routine Description:
free the internal data buffer's memory
Arguments:
None
Return Value:
none
--*/
{
if ( _pbData == NULL )
{
return;
}
if ( _fDynAllocated )
{
delete [] _pbData;
}
else
{
_pCurrentACache->Free( _pbData );
}
_pbData = NULL;
_cbData = 0;
}
FILTER_CHANNEL_CONTEXT::FILTER_CHANNEL_CONTEXT(
FILTER_CHANNEL *pManager
)
: _RawWriteData1Overlapped( UL_OVERLAPPED_CONTEXT_RAW_WRITE,
UL_OVERLAPPED_CONTEXT_DATA ),
_RawWriteData2Overlapped( UL_OVERLAPPED_CONTEXT_RAW_WRITE,
UL_OVERLAPPED_CONTEXT_DATA ),
_RawReadOverlapped( UL_OVERLAPPED_CONTEXT_RAW_READ ),
_AppWriteOverlapped( UL_OVERLAPPED_CONTEXT_APP_WRITE ),
_AppReadOverlapped( UL_OVERLAPPED_CONTEXT_APP_READ ),
_CloseOverlapped( UL_OVERLAPPED_CONTEXT_CLOSE ),
_buffConnectionInfo( _abConnectionInfo, sizeof( _abConnectionInfo ) ),
_fCloseConnection( FALSE ),
_cRefs( 1 ),
_cbRawReadData( 0 ),
_fNewConnection( TRUE ),
_pSSLContext( NULL ),
_lQueuedAppReads( 0 ),
_fAppReadInProgress( FALSE ),
_pISAPIContext( NULL ),
_cbNextRawReadSize( FILTER_CHANNEL::QueryDefaultRawReadSize() ),
_ulFilterBufferType( (HTTP_FILTER_BUFFER_TYPE) -1 ),
_pManager( pManager ),
_pEndpointConfig( NULL ),
_pLastAcquiredRawWriteOverlapped( NULL ),
_fTimerTickCountSet( FALSE )
{
_RawWriteData1Overlapped.SetContext( this );
_RawWriteData2Overlapped.SetContext( this );
_RawReadOverlapped.SetContext( this );
_AppWriteOverlapped.SetContext( this );
_AppReadOverlapped.SetContext( this );
_CloseOverlapped.SetContext( this ),
_RawWriteOverlappedFreeList.Next = NULL;
PushEntryList( &_RawWriteOverlappedFreeList, _RawWriteData1Overlapped.QueryListEntry() );
PushEntryList( &_RawWriteOverlappedFreeList, _RawWriteData2Overlapped.QueryListEntry() );
_dwLockIndex = 0;
_pManager->InsertFiltChannelContext(this);
_pConnectionInfo = (HTTP_RAW_CONNECTION_INFO*) _buffConnectionInfo.QueryPtr();
_pConnectionInfo->pInitialData = (PBYTE)_pConnectionInfo +
sizeof(HTTP_RAW_CONNECTION_INFO);
_pConnectionInfo->InitialDataSize = _buffConnectionInfo.QuerySize() -
sizeof(HTTP_RAW_CONNECTION_INFO);
// signature will be added by inheriting class
}
FILTER_CHANNEL_CONTEXT::~FILTER_CHANNEL_CONTEXT()
{
//
// Cleanup any attached stream context
//
if ( _pISAPIContext != NULL )
{
delete _pISAPIContext;
_pISAPIContext = NULL;
}
if ( _pSSLContext != NULL )
{
delete _pSSLContext;
_pSSLContext = NULL;
}
if ( _pEndpointConfig != NULL )
{
_pEndpointConfig->DereferenceEndpointConfig();
_pEndpointConfig = NULL;
}
//
// Manage the list of active UL_CONTEXTs
//
_pManager->RemoveFiltChannelContext(this);
}
VOID
FILTER_CHANNEL_CONTEXT::ReferenceFiltChannelContext(
VOID
)
/*++
Routine Description:
Reference the FILTER_CHANNEL_CONTEXT
Arguments:
none
Return Value:
none
--*/
{
LONG cRefs;
cRefs = InterlockedIncrement( &_cRefs );
#if DBG
//
// Log the reference ( sm_pTraceLog!=NULL if DBG=1)
//
_pManager->WriteRefTraceLog( cRefs,
this );
#endif
}
VOID
FILTER_CHANNEL_CONTEXT::DereferenceFiltChannelContext(
VOID
)
/*++
Routine Description:
Dereference (and possible destroy) the FILTER_CHANNEL_CONTEXT
Arguments:
none
Return Value:
none
--*/
{
LONG cRefs;
#if DBG
//
// We have to store pManager before Decrementing reference
// because after dereferencing it's illegal to access the data
// of the class because there is no guarantee that the instance
// still exists. Also it's necessary to guarantee that
// pManager will not be deleted before any thread executing
// this code is completed
// (Threadpool must be destroyed first - see the Terminate() call)
//
FILTER_CHANNEL * pManager = _pManager;
#endif
cRefs = InterlockedDecrement( &_cRefs );
#if DBG
pManager->WriteRefTraceLog( cRefs,
this );
#endif
if ( cRefs == 0 )
{
//
// CONTEXT is to be destroyed
// Remove it from the timer list so that there are
// are only valid entries on the list
//
StopTimeoutTimer();
delete this;
}
}
HRESULT
FILTER_CHANNEL_CONTEXT::OnAppReadCompletion(
DWORD /*cbCompletion*/,
DWORD dwCompletionStatus
)
/*++
Routine Description:
Completion for reads from an application
Arguments:
cbCompletion - Bytes of completion (currently not used)
dwCompletionStatus - Completion error
Return Value:
HRESULT
--*/
{
HTTP_FILTER_BUFFER * pFilterBuffer;
HRESULT hr = E_FAIL;
RAW_STREAM_INFO rawStreamInfo;
BOOL fComplete = FALSE;
BOOL fPostAppRead = FALSE;
//
// Just bail on errors
//
if ( dwCompletionStatus != NO_ERROR )
{
hr = HRESULT_FROM_WIN32( dwCompletionStatus );
goto ExitPoint;
}
pFilterBuffer = (HTTP_FILTER_BUFFER *) _ulAppReadFilterBuffer.pBuffer;
_ulFilterBufferType = pFilterBuffer->BufferType;
DBG_ASSERT( !_fNewConnection );
//
// If HTTP.SYS is telling us to close the connection, then do so now
//
if ( _ulFilterBufferType == HttpFilterBufferCloseConnection )
{
//
// Connection is to be closed gracefully
// One outstanding overlapped Raw Write may exists.
// Since HTTP.SYS will perform
// graceful disconnect it is safe to call StartClose() right now
// and no data from the outstanding RawWrite will be lost.
// Also the delayed ACK problem on the last send will not apply if close
// is requested while one RawWrite may still be outstanding
//
StartClose();
hr = S_OK;
goto ExitPoint;
}
//
// Setup raw stream descriptor
//
rawStreamInfo.pbBuffer = (PBYTE) pFilterBuffer->pBuffer;
rawStreamInfo.cbBuffer = pFilterBuffer->BufferSize;
rawStreamInfo.cbData = pFilterBuffer->BufferSize;
//
// First notify ISAPI filters if this is a stream from the application
// if _pISAPIContext is null then notification is disabled
if ( _ulFilterBufferType == HttpFilterBufferHttpStream &&
_pISAPIContext != NULL )
{
DBG_ASSERT( _pISAPIContext != NULL );
//
// We don't know what the raw write filter will do so we better
// have a thead available to prevent blocking
//
AddWorkerThread();
hr = _pISAPIContext->ProcessRawWriteData( &rawStreamInfo,
&fComplete );
RemoveWorkerThread();
if ( FAILED( hr ) )
{
goto ExitPoint;
}
if ( fComplete )
{
StartClose();
hr = S_OK;
goto ExitPoint;
}
}
//
// Next notify SSL filter always
//
DBG_ASSERT( _pSSLContext != NULL );
hr = _pSSLContext->ProcessRawWriteData( &rawStreamInfo,
&fComplete );
if ( FAILED( hr ) )
{
goto ExitPoint;
}
if ( fComplete )
{
StartClose();
hr = S_OK;
goto ExitPoint;
}
//
// If there is data to send to the client, then do so now.
// This check is done because the filter may decide to eat up all the
// data to be sent
//
if ( _ulFilterBufferType == HttpFilterBufferHttpStream &&
rawStreamInfo.pbBuffer != NULL &&
rawStreamInfo.cbData != 0 )
{
//
// If we got to here, then we have processed data to send to the client
//
//
// The processed data that is passed along will be written
// before our read buffer is posted.
//
hr = DoRawWrite( UL_CONTEXT_FLAG_ASYNC,
rawStreamInfo.pbBuffer,
rawStreamInfo.cbData,
NULL );
if ( FAILED( hr ) )
{
goto ExitPoint;
}
//
// Try to post another AppRead if there is one queued
// (by other words if multiple overlapped RawWrites limit was not reached)
//
fPostAppRead = FALSE;
_AppReadQueueLock.WriteLock();
if( _lQueuedAppReads != 0 )
{
fPostAppRead = TRUE;
//
// Only one thread a time is allowed to post new AppRead
//
_lQueuedAppReads--;
}
else
{
//
// We are not allowed to post new AppRead now because there are
// probably 2 outstanding RawWrites at this moment
//
_fAppReadInProgress = FALSE;
}
_AppReadQueueLock.WriteUnlock();
}
else
{
//
// No data was written
// we can safely make another AppRead
// because we still hold _fAppReadInProgress set to TRUE
//
fPostAppRead = TRUE;
}
if ( fPostAppRead )
{
//
// Kick off another app read
//
_ulAppReadFilterBuffer.pBuffer = _AppReadOverlapped.QueryDataBuffer();
_ulAppReadFilterBuffer.BufferSize = _AppReadOverlapped.QueryDataBufferSize();
hr = DoAppRead( UL_CONTEXT_FLAG_ASYNC,
&_ulAppReadFilterBuffer,
NULL );
goto ExitPoint;
}
else
{
//
// Raw Write completion will post app read
//
hr = S_OK;
goto ExitPoint;
}
ExitPoint:
//
// Close connection if error was detected
//
if ( FAILED( hr ) )
{
StartClose();
}
//
// Async App Read grabbed the reference. It is time to dereference now
//
DereferenceFiltChannelContext();
return hr;
}
HRESULT
FILTER_CHANNEL_CONTEXT::TryAppRead(
VOID
)
/*++
Routine Description:
Only up to one outstanding AppRead is allowed in any moment
because otherwise the order of data blocks sent out is not guaranteed
This method takes care of handling queued AppReads(). If there
is no AppRead in progress, it will try to get exclusive right
to process AppRead and if it succeeds then new AppRead is posted
Otherwise it will simply return with NO_ERROR;
Arguments:
Return Value:
HRESULT
--*/
{
BOOL fPostAppRead = FALSE;
_AppReadQueueLock.WriteLock();
if( !_fAppReadInProgress )
{
_fAppReadInProgress = TRUE;
//
// This thread was chosen to post app read
//
fPostAppRead = TRUE;
}
else
{
//
// Queue AppRead.
// Currently there is AppRead in progress and new one can start
// only after it completed and made RawWrite call
//
_lQueuedAppReads++;
}
_AppReadQueueLock.WriteUnlock();
if ( fPostAppRead )
{
//
// Kick off another app read
//
_ulAppReadFilterBuffer.pBuffer = _AppReadOverlapped.QueryDataBuffer();
_ulAppReadFilterBuffer.BufferSize = _AppReadOverlapped.QueryDataBufferSize();
return DoAppRead( UL_CONTEXT_FLAG_ASYNC,
&_ulAppReadFilterBuffer,
NULL );
}
return NO_ERROR;
}
HRESULT
FILTER_CHANNEL_CONTEXT::OnNewConnection(
VOID
)
/*++
Routine Description:
Handle initial completion for the HttpFilterAccept()
It is called from OnRawReadCompletion because New connection
completion is special case of RawWriteCompletion
Arguments:
cbCompletion - Bytes of completion
dwCompletionStatus - Completion error
Return Value:
HRESULT
--*/
{
HRESULT hr = E_FAIL;
_fNewConnection = FALSE;
//
// This is a new connection. We have one less FILTER_CHANNEL_CONTEXT to
// listen for incoming requests. Correct that if necessary.
//
_pManager->DecrementAndManageOutstandingContexts();
//
// Convert the UL addresses into something nicer!
//
HTTP_TRANSPORT_ADDRESS *pAddress = &_pConnectionInfo->Address;
_connectionContext.LocalAddressType = pAddress->pLocalAddress->sa_family;
_connectionContext.RemoteAddressType = pAddress->pRemoteAddress->sa_family;
if( pAddress->pLocalAddress->sa_family == AF_INET )
{
_connectionContext.SockLocalAddress.ipv4SockAddress
= * (PSOCKADDR_IN)pAddress->pLocalAddress;
}
else if( pAddress->pLocalAddress->sa_family == AF_INET6 )
{
_connectionContext.SockLocalAddress.ipv6SockAddress
= * (PSOCKADDR_IN6)pAddress->pLocalAddress;
}
else
{
DBG_ASSERT( FALSE );
}
if( pAddress->pRemoteAddress->sa_family == AF_INET )
{
_connectionContext.SockRemoteAddress.ipv4SockAddress
= * (PSOCKADDR_IN)pAddress->pRemoteAddress;
}
else if( pAddress->pRemoteAddress->sa_family == AF_INET6 )
{
_connectionContext.SockRemoteAddress.ipv6SockAddress
= * (PSOCKADDR_IN6)pAddress->pRemoteAddress;
}
else
{
DBG_ASSERT( FALSE );
}
_connectionContext.fIsSecure = FALSE;
_connectionContext.RawConnectionId = _pConnectionInfo->ConnectionId;
//
// create ISAPI_STREAM_CONTEXT
// if ISAPI filters are enabled
//
// Endpoint config may contain information about explicitly
// disabling raw ISAPI filter handling
//
if ( _pManager->QueryNotifyISAPIFilters() )
{
//
// Lookup the endpoint config
//
hr = ENDPOINT_CONFIG::GetEndpointConfig( &_connectionContext,
&_pEndpointConfig,
TRUE /*fCreateEmptyIfNotFound*/);
if ( FAILED( hr ) )
{
return hr;
}
DBG_ASSERT( _pEndpointConfig != NULL );
//
// HttpApi Config store may contain endpoint flag
// that will prevent raw ISAPI Filter execution
// on the endpoint data
// Create ISAPI context only if Raw Filter handling is enabled
//
if ( !_pEndpointConfig->QueryNoRawFilter() )
{
hr =ISAPI_STREAM_CONTEXT::CreateContext(
this,
&_pISAPIContext );
if ( hr == HRESULT_FROM_WIN32( ERROR_SERVICE_NOT_ACTIVE ) )
{
//
// this error means that w3svc is shutting down so
// there is no more need to create ISAPI_STREAM_CONTEXT
//
_pISAPIContext = NULL;
}
else if ( FAILED( hr ) )
{
return hr;
}
}
}
if ( _pISAPIContext != NULL )
{
_connectionContext.pfnSendDataBack = ISAPI_STREAM_CONTEXT::SendDataBack;
}
_connectionContext.pvStreamContext = this;
//
// copy out the server name.
//
_connectionContext.ClientSSLContextLength =
_pConnectionInfo->ClientSSLContextLength;
_connectionContext.pClientSSLContext =
_pConnectionInfo->pClientSSLContext;
//
// Fill in our read buffer (as if we had read it in directly)
//
_cbRawReadData = _pConnectionInfo->InitialDataSize;
if ( !_RawReadOverlapped.ResizeDataBuffer(
max( _pConnectionInfo->InitialDataSize,
QueryNextRawReadSize() ) ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
memcpy( _RawReadOverlapped.QueryDataBuffer(),
_pConnectionInfo->pInitialData,
_pConnectionInfo->InitialDataSize );
//
// First indicate a new connection
//
DBG_ASSERT( _pSSLContext != NULL );
//
// We pass _pEndpointConfig but it may be empty.
// In that case SSL Context will have to make a lookup
//
hr = _pSSLContext->ProcessNewConnection( &_connectionContext,
_pEndpointConfig );
if ( FAILED( hr ) )
{
return hr;
}
//
// if _pISAPIContext == NULL then ISAPI notification
// was not requested
//
if ( _pISAPIContext != NULL )
{
//
// We don't know what the raw write filter will do so we better
// have a thead available to prevent blocking
//
hr = _pISAPIContext->ProcessNewConnection(
&_connectionContext,
_pEndpointConfig
);
if ( FAILED( hr ) )
{
return hr;
}
}
//
// Adjust AppRead buffer size
//
if ( !_AppReadOverlapped.ResizeDataBuffer(
FILTER_CHANNEL::QueryDefaultAppReadSize() ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
//
// Post one AppRead and queue another one
// This will enable 2 outstanding RawWrites to exist
// AppReads will be executed synchronously, so that
// we don't lose order of incoming data but we will
// not wait for completion of each RawWrite before
// starting New AppRead
//
_lQueuedAppReads = 1;
_fAppReadInProgress = TRUE;
_ulAppReadFilterBuffer.pBuffer = _AppReadOverlapped.QueryDataBuffer();
_ulAppReadFilterBuffer.BufferSize = _AppReadOverlapped.QueryDataBufferSize();
return DoAppRead( UL_CONTEXT_FLAG_ASYNC,
&_ulAppReadFilterBuffer,
NULL );
}
HRESULT
FILTER_CHANNEL_CONTEXT::OnRawReadCompletion(
DWORD cbCompletion,
DWORD dwCompletionStatus
)
/*++
Routine Description:
Get read completions off the wire. This includes the initial
completion for the HttpFilterAccept()
Arguments:
cbCompletion - Bytes of completion
dwCompletionStatus - Completion error
Return Value:
HRESULT
--*/
{
HRESULT hr;
BOOL fReadMore = FALSE;
BOOL fComplete = FALSE;
RAW_STREAM_INFO rawStreamInfo;
//
// Handle errors
//
if ( dwCompletionStatus != NO_ERROR )
{
if ( _fNewConnection )
{
_pManager->DecrementAndManageOutstandingContexts();
}
hr = HRESULT_FROM_WIN32( dwCompletionStatus );
goto ExitPoint;
}
if ( cbCompletion == 0 )
{
//
// Handle graceful disconnect
//
_ulAppWriteFilterBuffer.BufferType = HttpFilterBufferNotifyDisconnect;
_ulAppWriteFilterBuffer.pBuffer = NULL;
_ulAppWriteFilterBuffer.BufferSize = 0;
//
// no more RawRead will be launched
//
hr = DoAppWrite(UL_CONTEXT_FLAG_ASYNC, &_ulAppWriteFilterBuffer, NULL);
goto ExitPoint;
}
//
// If this is a new connection, then grok connection information, and
// maintain pending count
//
if ( _fNewConnection )
{
hr = OnNewConnection( );
if ( FAILED( hr ) )
{
goto ExitPoint;
}
}
else
{
_cbRawReadData += cbCompletion;
}
//
// reset default raw read size
//
SetNextRawReadSize( DEFAULT_RAW_READ_SIZE );
rawStreamInfo.pbBuffer = _RawReadOverlapped.QueryDataBuffer();
rawStreamInfo.cbBuffer = _RawReadOverlapped.QueryDataBufferSize();
rawStreamInfo.cbData = _cbRawReadData;
//
// First, we will notify SSL
//
DBG_ASSERT( _pSSLContext != NULL );
hr = _pSSLContext->ProcessRawReadData( &rawStreamInfo,
&fReadMore,
&fComplete );
if ( FAILED( hr ) )
{
goto ExitPoint;
}
_cbRawReadData = rawStreamInfo.cbData;
//
// If we need to read more data, then do so now
//
if ( fReadMore )
{
//
// rawStreamInfo.pbBuffer may have been replaced by different buffer
// in ProcessRawReadData() call.
// copy data back to pbuffRawReadData
//
if ( rawStreamInfo.pbBuffer != _RawReadOverlapped.QueryDataBuffer() )
{
if ( !_RawReadOverlapped.ResizeDataBuffer( rawStreamInfo.cbData +
QueryNextRawReadSize() ) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto ExitPoint;
}
memmove( _RawReadOverlapped.QueryDataBuffer(),
rawStreamInfo.pbBuffer,
rawStreamInfo.cbData
);
}
else
{
// no need to copy data but we still have to resize
//
if ( !_RawReadOverlapped.ResizeDataBuffer( rawStreamInfo.cbData +
QueryNextRawReadSize() ) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto ExitPoint;
}
}
hr = DoRawRead( UL_CONTEXT_FLAG_ASYNC,
_RawReadOverlapped.QueryDataBuffer() + _cbRawReadData,
QueryNextRawReadSize(),
NULL );
goto ExitPoint;
}
if ( fComplete )
{
StartClose();
hr = S_OK;
goto ExitPoint;
}
//
// Reset the next read size before calling into filters since SSL may
// have done a really small read, just previous to this.
//
SetNextRawReadSize( FILTER_CHANNEL::QueryDefaultRawReadSize() );
//
// Now we can start notifying ISAPI filters if needed (and there is
// data to process)
//
if ( _pISAPIContext != NULL )
{
fComplete = FALSE;
fReadMore = FALSE;
AddWorkerThread();
hr = _pISAPIContext->ProcessRawReadData( &rawStreamInfo,
&fReadMore,
&fComplete );
RemoveWorkerThread();
if ( FAILED( hr ) )
{
goto ExitPoint;
}
_cbRawReadData = rawStreamInfo.cbData;
//
// If we need to read more data, then do so now
//
if ( fReadMore )
{
//
// rawStreamInfo may have been replaced by different buffer
// in ProcessRawReadData() call.
// copy data back to pbufRawReadData
//
if ( rawStreamInfo.pbBuffer != _RawReadOverlapped.QueryDataBuffer() )
{
if ( !_RawReadOverlapped.ResizeDataBuffer( rawStreamInfo.cbData +
QueryNextRawReadSize() ) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto ExitPoint;
}
memmove( _RawReadOverlapped.QueryDataBuffer(),
rawStreamInfo.pbBuffer,
rawStreamInfo.cbData
);
}
else
{
// no need to copy data but we still have to resize
//
if ( !_RawReadOverlapped.ResizeDataBuffer( rawStreamInfo.cbData +
QueryNextRawReadSize() ) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto ExitPoint;
}
}
hr = DoRawRead( UL_CONTEXT_FLAG_ASYNC,
_RawReadOverlapped.QueryDataBuffer() + _cbRawReadData,
QueryNextRawReadSize(),
NULL );
goto ExitPoint;
}
if ( fComplete )
{
StartClose();
hr = S_OK;
goto ExitPoint;
}
}
//
// If after filtering there is data remaining in our buffer, then that
// data is destined to the application. Send it asynchronously because
// there is a risk that synchronous call gets blocked for a long time
//
_cbRawReadData = 0;
if ( rawStreamInfo.cbData != 0 )
{
//
// Reset default raw read size
//
SetNextRawReadSize( FILTER_CHANNEL::QueryDefaultRawReadSize() );
if ( !_RawReadOverlapped.ResizeDataBuffer( QueryNextRawReadSize() ) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto ExitPoint;
}
//
// Initiate a raw read that will send the data before posting the read.
//
_ulAppWriteAndRawReadFilterBuffer.BufferType = HttpFilterBufferHttpStream;
_ulAppWriteAndRawReadFilterBuffer.pBuffer = _RawReadOverlapped.QueryDataBuffer();
_ulAppWriteAndRawReadFilterBuffer.BufferSize = QueryNextRawReadSize();
_ulAppWriteAndRawReadFilterBuffer.pWriteBuffer = rawStreamInfo.pbBuffer;
_ulAppWriteAndRawReadFilterBuffer.WriteBufferSize = rawStreamInfo.cbData;
_ulAppWriteAndRawReadFilterBuffer.Reserved = _pConnectionInfo->ConnectionId;
hr = DoAppWriteAndRawRead(UL_CONTEXT_FLAG_ASYNC, &_ulAppWriteAndRawReadFilterBuffer);
if ( FAILED( hr ) )
{
goto ExitPoint;
}
}
else
{
//
// Kick off another raw read
//
//
// reset default raw read size
//
SetNextRawReadSize( FILTER_CHANNEL::QueryDefaultRawReadSize() );
if ( !_RawReadOverlapped.ResizeDataBuffer( QueryNextRawReadSize() ) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto ExitPoint;
}
hr = DoRawRead( UL_CONTEXT_FLAG_ASYNC,
_RawReadOverlapped.QueryDataBuffer(),
QueryNextRawReadSize() ,
NULL );
if ( FAILED( hr ) )
{
goto ExitPoint;
}
}
hr = S_OK;
ExitPoint:
//
// Close connection if error was detected
//
if ( FAILED( hr ) )
{
StartClose();
}
//
// Async Raw Read grabbed the reference.
// It is time to dereference now
//
DereferenceFiltChannelContext();
return hr;
}
HRESULT
FILTER_CHANNEL_CONTEXT::OnRawWriteCompletion(
DWORD /*cbCompletion*/,
DWORD dwCompletionStatus,
UL_OVERLAPPED_CONTEXT * pContextOverlapped
)
/*++
Routine Description:
after raw write completes, this routine has to assure that new asynchronous AppRead request is
made to continue properly in communication
Note: This completion should be caused only by asynchronous DoRawWrite started in completion routine
of AppRead (OnAppReadCompletion()).
Please assure that NO RawWrite that is initiated by data coming from RawRead (SSL handshake)
will be called asynchronously. That could cause race condition (multiple threads using the same buffer
eg. for SSL data encryption)
Arguments:
cbCompletion - Bytes of completion (currently not used)
dwCompletionStatus - Completion error
Return Value:
HRESULT
--*/
{
HRESULT hr = E_FAIL;
switch ( pContextOverlapped->QuerySubtype() )
{
case UL_OVERLAPPED_CONTEXT_COMPLETION_CALLBACK:
{
//
// delete context buffer allocated by SSPI package
//
hr = pContextOverlapped->DoCallback();
//
// time to delete temporary overlapped context
//
delete pContextOverlapped;
pContextOverlapped = NULL;
// if completion status failed then return completion
// status, otherwise hr of the DoCallback
if ( dwCompletionStatus != NO_ERROR )
{
break;
}
else
{
goto ExitPoint;
}
}
case UL_OVERLAPPED_CONTEXT_TEMPORARY:
//
// time to delete temporary overlapped context
//
delete pContextOverlapped;
pContextOverlapped = NULL;
break;
case UL_OVERLAPPED_CONTEXT_DATA:
//
// make overlapped to be available for next useq
//
ReleaseRawWriteBuffer( pContextOverlapped );
if ( dwCompletionStatus != NO_ERROR )
{
break;
}
//
// raw write completion triggers new AppRead
// but there is only one outstanding AppRead allowed
// a time ( while we have 2 outstanding RawWrites
// to emilinate the delayed ACK problem - see Windows Bugs 394511 )
//
hr = TryAppRead();
goto ExitPoint;
default:
DBG_ASSERT(FALSE);
hr = E_FAIL;
goto ExitPoint;
}
//
// completion status must be returned
//
hr = HRESULT_FROM_WIN32( dwCompletionStatus );
ExitPoint:
//
// Close connection if error was detected
//
if ( FAILED( hr ) )
{
StartClose();
}
//
// Async Raw Write grabbed the reference.
// It is time to dereference now
//
DereferenceFiltChannelContext();
return hr;
}
HRESULT
FILTER_CHANNEL_CONTEXT::OnAppWriteCompletion(
DWORD /*cbCompletion*/,
DWORD dwCompletionStatus
)
/*++
Routine Description:
OnAppWrite completion should be called only after handling
graceful disconnect. All the other AppWrites must happen with
DoAppWriteAndRawRead()
Arguments:
cbCompletion - Bytes of completion (currently not used)
dwCompletionStatus - Completion error
Return Value:
HRESULT
--*/
{
HRESULT hr = HRESULT_FROM_WIN32( dwCompletionStatus );
//
// Close connection if error was detected
//
if ( FAILED( hr ) )
{
StartClose();
}
//
// Async App Write grabbed the reference.
// It is time to dereference now
//
DereferenceFiltChannelContext();
return hr;
}
HRESULT
FILTER_CHANNEL_CONTEXT::DoAppWriteAndRawRead(
DWORD dwFlags,
PHTTP_FILTER_BUFFER_PLUS pHttpBufferPlus
)
/*++
Routine Description:
Write data to the app and read some bytes from the wire
Arguments:
dwFlags - UL_CONTEXT_ASYNC for async
pHttpBufferPlus - read and write buffers
Return Value:
HRESULT
--*/
{
BOOL fAsync = !!( dwFlags & UL_CONTEXT_FLAG_ASYNC );
ULONG ulRet = ERROR_SUCCESS;
HRESULT hr = NO_ERROR;
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"ENTER DoAppWriteAndRawRead( async:%d )\n",
fAsync
));
}
if ( fAsync )
{
ReferenceFiltChannelContext();
}
ulRet = HttpFilterAppWriteAndRawRead( _pManager->QueryFilterHandle(),
pHttpBufferPlus,
fAsync ? QueryRawReadOverlapped() : NULL);
if ( fAsync )
{
if ( ulRet == NO_ERROR )
{
ulRet = ERROR_IO_PENDING;
}
if ( ulRet != ERROR_IO_PENDING )
{
DereferenceFiltChannelContext();
hr = HRESULT_FROM_WIN32( ulRet );
}
}
else
{
if ( ulRet != NO_ERROR )
{
hr = HRESULT_FROM_WIN32( ulRet );
}
}
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"LEAVE DoAppWriteAndRawRead( async:%d )\n",
fAsync
));
}
return hr;
}
HRESULT
FILTER_CHANNEL_CONTEXT::DoRawWrite(
DWORD dwFlags,
PVOID pvBuffer,
DWORD cbBuffer,
DWORD * pcbWritten,
UL_OVERLAPPED_CONTEXT::PFN_CALLBACK pfnCallback,
PVOID pCallbackParam
)
/*++
Routine Description:
Write some bytes to the wire
Arguments:
dwFlags - UL_CONTEXT_ASYNC for async
pvBuffer - Buffer to send
cbBuffer - bytes in buffer
pcbWritten - Bytes written
pfnCallback - optional Callback function - default is NULL
pCallbackParam - callback parameter -default is NULL
Return Value:
HRESULT
--*/
{
BOOL fAsync = !!( dwFlags & UL_CONTEXT_FLAG_ASYNC );
ULONG ulRet = ERROR_SUCCESS;
HRESULT hr = NO_ERROR;
OVERLAPPED * pOverlapped = NULL;
UL_OVERLAPPED_CONTEXT * pContextOverlappedBuffered = NULL;
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"ENTER DoRawWrite( async:%d, buffered:%d, bytes:%d )\n",
fAsync,
!!( dwFlags & UL_CONTEXT_FLAG_BUFFERED ),
cbBuffer
));
}
if ( fAsync )
{
if ( dwFlags & UL_CONTEXT_FLAG_COMPLETION_CALLBACK )
{
//
// Create new temporary overlapped context
//
pContextOverlappedBuffered
= new UL_OVERLAPPED_CONTEXT( UL_OVERLAPPED_CONTEXT_RAW_WRITE,
UL_OVERLAPPED_CONTEXT_COMPLETION_CALLBACK );
if ( pContextOverlappedBuffered == NULL )
{
hr = HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY );
goto Finished;
}
//
// Configure new overlapped context
//
pContextOverlappedBuffered->SetContext( this );
pContextOverlappedBuffered->SetCallBack( pfnCallback,
pCallbackParam );
//
// retrieve overlapped to be used for this RawWrite
//
pOverlapped = pContextOverlappedBuffered->QueryOverlapped();
}
else if ( dwFlags & UL_CONTEXT_FLAG_BUFFERED )
{
//
// BUFFERED flag is set - we will make private copy
// of the data to be sent to enable caller to use
// it's buffer upon completion
//
//
// Create new temporary overlapped context
//
pContextOverlappedBuffered
= new UL_OVERLAPPED_CONTEXT( UL_OVERLAPPED_CONTEXT_RAW_WRITE,
UL_OVERLAPPED_CONTEXT_TEMPORARY );
if ( pContextOverlappedBuffered == NULL )
{
hr = HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY );
goto Finished;
}
//
// Configure new overlapped context
//
pContextOverlappedBuffered->SetContext( this );
//
// copy data to be sent to private buffer
// that is member of UL_OVERLAPPED_CONTEXT
//
if ( !pContextOverlappedBuffered->ResizeDataBuffer( cbBuffer ) )
{
hr = HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY );
goto Finished;
}
memcpy( pContextOverlappedBuffered->QueryDataBuffer(),
(PBYTE) pvBuffer,
cbBuffer );
//
// instead of data buffer passed by caller our private
// buffer will be used. Thus caller will be able to reclaim
// it's buffer after returing from this call without
// waiting for completion
//
pvBuffer = pContextOverlappedBuffered->QueryDataBuffer();
//
// retrieve overlapped to be used for this RawWrite
//
pOverlapped = pContextOverlappedBuffered->QueryOverlapped();
}
else
{
//
// Not buffered send.
// We have to determine which Overlapped context to use
// If pvBuffer was acquired by AcquireRawWriteBuffer()
// then _pLastAcquiredRawWriteOverlapped is not NULL
// and is pointing to the structure that contains the acquired buffer
//
UL_OVERLAPPED_CONTEXT * pRawWriteUlOverlapped = QueryLastAcquiredRawWriteBufferOverlapped();
if ( pRawWriteUlOverlapped == NULL ||
pRawWriteUlOverlapped->QueryDataBuffer() != (PBYTE) pvBuffer )
{
//
// This data must be coming from rawdata filter
// when SSL is not used (SSL will always use
// buffers that are returned by AcquireRawWriteBuffer())
//
// It is necessary to copy data to private buffer
// in order to be able to maintain 2 outstanding
// RawWrites
//
PBYTE pbRawWriteBuffer = NULL;
hr = AcquireRawWriteBuffer( &pbRawWriteBuffer,
cbBuffer );
if ( FAILED( hr ) )
{
goto Finished;
}
memcpy( pbRawWriteBuffer,
(PBYTE) pvBuffer,
cbBuffer );
pvBuffer = (PVOID) pbRawWriteBuffer;
//
// now it's guaranteed that we use one of the
// DATA1 or DATA2 buffers for RawWrite
// let's find out the associated overlapped
//
pRawWriteUlOverlapped =
QueryLastAcquiredRawWriteBufferOverlapped();
DBG_ASSERT( pRawWriteUlOverlapped != NULL );
}
pOverlapped = pRawWriteUlOverlapped->QueryOverlapped();
}
//
// For all asynchonous we have to reference FiltChannelContext
//
ReferenceFiltChannelContext();
}
else
{
pOverlapped = NULL;
}
ulRet = HttpFilterRawWrite( _pManager->QueryFilterHandle(),
_pConnectionInfo->ConnectionId,
pvBuffer,
cbBuffer,
pcbWritten,
pOverlapped );
if ( fAsync )
{
if ( ulRet == NO_ERROR )
{
ulRet = ERROR_IO_PENDING;
}
if ( ulRet != ERROR_IO_PENDING )
{
DereferenceFiltChannelContext();
hr = HRESULT_FROM_WIN32( ulRet );
goto Finished;
}
else
{
hr = NO_ERROR;
}
}
else
{
//
// for synchronous sends the raw write buffers must
// be released if they are owned by the
// FILTER_CHANNEL_CONTEXT
//
TryReleaseLastAcquiredRawWriteBuffer();
if ( ulRet != NO_ERROR )
{
hr = HRESULT_FROM_WIN32( ulRet );
goto Finished;
}
}
Finished:
if ( FAILED( hr ) )
{
if ( pContextOverlappedBuffered != NULL )
{
delete pContextOverlappedBuffered;
pContextOverlappedBuffered = NULL;
}
}
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"LEAVE DoRawWrite( async:%d, bytes:%d ) hr=0x%x\n",
fAsync,
cbBuffer,
hr
));
}
return hr;
}
HRESULT
FILTER_CHANNEL_CONTEXT::DoAppRead(
DWORD dwFlags,
HTTP_FILTER_BUFFER * pFilterBuffer,
DWORD * pcbRead
)
/*++
Routine Description:
Read data from application
Arguments:
dwFlags - UL_CONTEXT_ASYNC for async
pFilterBuffer - Filter buffer
pcbRead - Bytes read
Return Value:
HRESULT
--*/
{
BOOL fAsync = !!( dwFlags & UL_CONTEXT_FLAG_ASYNC );
ULONG ulRet = ERROR_SUCCESS;
HRESULT hr = NO_ERROR;
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"ENTER DoAppRead( async:%d )\n",
fAsync
));
}
if ( fAsync )
{
ReferenceFiltChannelContext();
}
ulRet = HttpFilterAppRead( _pManager->QueryFilterHandle(),
_pConnectionInfo->ConnectionId,
pFilterBuffer,
pFilterBuffer->BufferSize,
pcbRead,
fAsync ? QueryAppReadOverlapped() : NULL);
if ( fAsync )
{
if ( ulRet == NO_ERROR )
{
ulRet = ERROR_IO_PENDING;
}
if ( ulRet != ERROR_IO_PENDING )
{
DereferenceFiltChannelContext();
hr = HRESULT_FROM_WIN32( ulRet );
}
}
else
{
if ( ulRet != NO_ERROR )
{
hr = HRESULT_FROM_WIN32( ulRet );
}
}
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"LEAVE DoAppRead( async:%d )\n",
fAsync
));
}
return hr;
}
HRESULT
FILTER_CHANNEL_CONTEXT::DoAppWrite(
DWORD dwFlags,
HTTP_FILTER_BUFFER * pFilterBuffer,
DWORD * pcbWritten
)
/*++
Routine Description:
Write data to the application
Arguments:
dwFlags - UL_CONTEXT_ASYNC for async
pFilterBuffer - Filter buffer
pcbWritten - Bytes written
Return Value:
HRESULT
--*/
{
BOOL fAsync = !!( dwFlags & UL_CONTEXT_FLAG_ASYNC );
ULONG ulRet = ERROR_SUCCESS;
HRESULT hr = NO_ERROR;
DBG_ASSERT( pFilterBuffer != NULL );
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"ENTER DoAppWrite( async:%d, bytes:%d, buffertype:%d )\n",
fAsync,
pFilterBuffer->BufferSize,
pFilterBuffer->BufferType
));
}
if ( fAsync )
{
ReferenceFiltChannelContext();
}
ulRet = HttpFilterAppWrite( _pManager->QueryFilterHandle(),
_pConnectionInfo->ConnectionId,
pFilterBuffer,
pFilterBuffer->BufferSize,
pcbWritten,
fAsync ? QueryAppWriteOverlapped() : NULL );
if ( fAsync )
{
if ( ulRet == NO_ERROR )
{
ulRet = ERROR_IO_PENDING;
}
if ( ulRet != ERROR_IO_PENDING )
{
DereferenceFiltChannelContext();
hr = HRESULT_FROM_WIN32( ulRet );
}
}
else
{
if ( ulRet != NO_ERROR )
{
hr = HRESULT_FROM_WIN32( ulRet );
}
}
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"LEAVE DoAppWrite( async:%d, bytes:%d, buffertype:%d ) hr=0x%x\n",
fAsync,
pFilterBuffer->BufferSize,
pFilterBuffer->BufferType,
hr
));
}
return hr;
}
HRESULT
FILTER_CHANNEL_CONTEXT::DoRawRead(
DWORD dwFlags,
PVOID pvBuffer,
DWORD cbBuffer,
DWORD * pcbWritten
)
/*++
Routine Description:
Read some bytes from the wire
Arguments:
dwFlags - UL_CONTEXT_ASYNC for async
pvBuffer - buffer
cbBuffer - bytes in buffer
pcbWritten - Bytes written
Return Value:
HRESULT
--*/
{
BOOL fAsync = !!( dwFlags & UL_CONTEXT_FLAG_ASYNC );
ULONG ulRet = ERROR_SUCCESS;
HRESULT hr = NO_ERROR;
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"ENTER DoRawRead( async:%d )\n",
fAsync
));
}
if ( fAsync )
{
ReferenceFiltChannelContext();
}
ulRet = HttpFilterRawRead( _pManager->QueryFilterHandle(),
_pConnectionInfo->ConnectionId,
pvBuffer,
cbBuffer,
pcbWritten,
fAsync ? QueryRawReadOverlapped() : NULL);
if ( fAsync )
{
if ( ulRet == NO_ERROR )
{
ulRet = ERROR_IO_PENDING;
}
if ( ulRet != ERROR_IO_PENDING )
{
DereferenceFiltChannelContext();
hr = HRESULT_FROM_WIN32( ulRet );
}
}
else
{
if ( ulRet != NO_ERROR )
{
hr = HRESULT_FROM_WIN32( ulRet );
}
}
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"LEAVE DoRawRead( async:%d )\n",
fAsync
));
}
return hr;
}
VOID
FILTER_CHANNEL_CONTEXT::StartClose(
VOID
)
/*++
Routine Description:
Start the process of closing the connection (and cleaning up FILTER_CHANNEL_CONTEXT)
Arguments:
None
Return Value:
None
--*/
{
BOOL fOld;
fOld = (BOOL) InterlockedCompareExchange( (PLONG) &_fCloseConnection,
TRUE,
FALSE );
if ( fOld == FALSE )
{
// _fNewConnection = TRUE indicates that
// there should not be valid connection info so there is nothing to be closed
if( _pManager->QueryFilterHandle() != NULL && !_fNewConnection )
{
ULONG ulRet = ERROR_SUCCESS;
//
// We don't have to grab the reference because
// we use the initial connection reference
//
ulRet = HttpFilterClose( _pManager->QueryFilterHandle(),
_pConnectionInfo->ConnectionId,
QueryCloseOverlapped() );
if ( ulRet == NO_ERROR )
{
//
// Both NO_ERROR and ERROR_IO_PENDING
// are expected to go the completion path
//
ulRet = ERROR_IO_PENDING;
}
if ( ulRet != ERROR_IO_PENDING )
{
//
// Note: ERROR_INVALID_PARAMETER may be received if client
// already closed the connection (connection ID will be considered invalid
// from the http.sys point of view).
//
// Notify about closing and do the final dereference
//
CloseNotify();
DereferenceFiltChannelContext();
}
}
else
{
//
// We were the ones to set the flag. Do the final dereference
//
DereferenceFiltChannelContext();
}
}
else
{
//
// Someone else has set the flag. Let them dereference
//
}
}
HRESULT
FILTER_CHANNEL_CONTEXT::DoAccept(
VOID
)
/*++
Routine Description:
Accept an incoming connection by calling HttpFilterAccept()
Arguments:
None
Return Value:
HRESULT
--*/
{
ULONG ulRet;
HRESULT hr = NO_ERROR;
ReferenceFiltChannelContext();
ulRet = HttpFilterAccept( _pManager->QueryFilterHandle(),
_pConnectionInfo,
_buffConnectionInfo.QuerySize(),
NULL,
QueryRawReadOverlapped() );
if ( ulRet != ERROR_IO_PENDING )
{
hr = HRESULT_FROM_WIN32( ulRet );
DereferenceFiltChannelContext();
DBGPRINTF(( DBG_CONTEXT,
"Error calling HttpFilterAccept(). hr = %x\n",
hr ));
}
else
{
//
// Another outstanding context available!
//
_pManager->IncrementOutstandingContexts();
}
return hr;
}
HRESULT
FILTER_CHANNEL_CONTEXT::SendDataBack(
RAW_STREAM_INFO * pRawStreamInfo
)
/*++
Routine Description:
Sends given data back to client, while going with the ssl filter
if necessary
Arguments:
pRawStreamInfo - Raw data to send back
Return Value:
HRESULT
--*/
{
if ( pRawStreamInfo == NULL )
{
DBG_ASSERT( FALSE );
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
}
DBG_ASSERT( _pSSLContext != NULL );
//
// ISAPI filter has sent back some data in a raw notification.
// Have SSL process it and then send it here
//
HRESULT hr = _pSSLContext->SendDataBack( pRawStreamInfo );
if ( FAILED( hr ) )
{
return hr;
}
//
// Send back the data
//
// bump up the number of threads because
// this is synchronous call that may block our thread
// for quite a while
//
AddWorkerThread();
hr = DoRawWrite( UL_CONTEXT_FLAG_SYNC,
pRawStreamInfo->pbBuffer,
pRawStreamInfo->cbData,
NULL );
RemoveWorkerThread();
return hr;
}
VOID
FILTER_CHANNEL_CONTEXT::CloseNotify(
VOID
)
/*++
Routine Description:
Notify ISAPI Filter (if present) or whoever else interested
that connection was closed
Arguments:
None
Return Value:
VOID
--*/
{
//
// Notify ISAPIs of the close
//
if ( _pISAPIContext != NULL )
{
//
// We don't know what the raw write filter will do so we better
// have a thead available to prevent blocking
//
AddWorkerThread();
_pISAPIContext->ProcessConnectionClose();
RemoveWorkerThread();
}
}
//
// Constructor
//
FILTER_CHANNEL::FILTER_CHANNEL(
LPWSTR pwszFilterChannelName
)
{
_dwInitcsFiltChannelContexts = 0;
_cFiltChannelContexts = 0;
_hFilterHandle = NULL;
_lStartedListening = 0;
_pThreadPool = NULL;
_cDesiredOutstanding = 0;
_cOutstandingContexts = 0;
_lEnteredOutstandingContextsAddingLoop = 0;
_pTraceLog = NULL;
_lNotifyISAPIFilters = 0;
_pwszFilterChannelName = pwszFilterChannelName;
_hTimer = NULL;
_dwTotalFilterChannelContexts = 0;
};
//
// Destructor
//
FILTER_CHANNEL::~FILTER_CHANNEL()
{
}
HRESULT
FILTER_CHANNEL::Initialize(
VOID
)
/*++
Routine Description:
Global Initialization
Arguments:
None
Return Value:
HRESULT
--*/
{
ULONG ulRet = ERROR_SUCCESS;
HRESULT hr = NO_ERROR;
BOOL fRet = FALSE;
HKEY hKeyParam = NULL;
DWORD dwType = 0;
DWORD dwValue = 0;
for ( int i = 0; i < NUM_CS_FILT_CHANNEL_CONTEXTS; i ++ )
{
fRet = InitializeCriticalSectionAndSpinCount(
&_csFiltChannelContexts[ i ],
0x80000000 /* precreate event */ |
IIS_DEFAULT_CS_SPIN_COUNT );
if ( !fRet )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto Finished;
}
_dwInitcsFiltChannelContexts ++;
InitializeListHead( &_ListHead[ i ] );
InitializeListHead( &_TimerListHead[ i ] );
}
#if DBG
_pTraceLog = CreateRefTraceLog( 2000, 0 );
#endif
//
// Read registry parameters
//
if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
REGISTRY_KEY_HTTPFILTER_PARAMETERS_W,
0,
KEY_READ,
&hKeyParam ) == NO_ERROR )
{
DWORD dwBytes = sizeof( dwValue );
DWORD dwErr = RegQueryValueExW( hKeyParam,
SZ_REG_DEFAULT_RAW_READ_SIZE,
NULL,
&dwType,
( LPBYTE )&dwValue,
&dwBytes
);
if ( ( dwErr == ERROR_SUCCESS ) &&
( dwType == REG_DWORD ) )
{
sm_dwDefaultRawReadSize = dwValue;
}
dwBytes = sizeof( dwValue );
dwErr = RegQueryValueExW( hKeyParam,
SZ_REG_DEFAULT_APP_READ_SIZE,
NULL,
&dwType,
( LPBYTE )&dwValue,
&dwBytes
);
if ( ( dwErr == ERROR_SUCCESS ) &&
( dwType == REG_DWORD ) )
{
// sizeof(HTTP_FILTER_BUFFER) is added because
// compacted structure HTTP_FILTER_BUFFER is
// placed to provided buffer
//
sm_dwDefaultAppReadSize = dwValue + sizeof( HTTP_FILTER_BUFFER );
}
dwBytes = sizeof( dwValue );
dwErr = RegQueryValueExW( hKeyParam,
SZ_REG_CONTEXT_DESIRED_OUTSTANDING,
NULL,
&dwType,
( LPBYTE )&dwValue,
&dwBytes
);
if ( ( dwErr == ERROR_SUCCESS ) &&
( dwType == REG_DWORD ) )
{
sm_dwContextDesiredOutstanding = dwValue;
}
dwBytes = sizeof( dwValue );
dwErr = RegQueryValueExW( hKeyParam,
SZ_REG_ENABLE_TEMPORARY_BUFFERS,
NULL,
&dwType,
( LPBYTE )&dwValue,
&dwBytes
);
if ( ( dwErr == ERROR_SUCCESS ) &&
( dwType == REG_DWORD ) )
{
sm_fEnableTemporaryBuffers = !!dwValue;
}
dwBytes = sizeof( dwValue );
dwErr = RegQueryValueExW( hKeyParam,
SZ_RENEGOTIATION_TIMEOUT,
NULL,
&dwType,
( LPBYTE )&dwValue,
&dwBytes
);
if ( ( dwErr == ERROR_SUCCESS ) &&
( dwType == REG_DWORD ) )
{
sm_dwHandshakeTimeoutInSec = dwValue;
if ( sm_dwHandshakeTimeoutInSec > MAXDWORD / 1000 )
{
// Disable timers because
// the configured value is too large
//
sm_dwHandshakeTimeoutInSec = 0;
}
if ( sm_dwHandshakeTimeoutInSec > 0 &&
sm_dwHandshakeTimeoutInSec < MINIMUM_RENEGOTIATION_TIMEOUT_IN_SEC )
{
//
// MINIMUM_RENEGOTIATION_TIMEOUT_IN_SEC
// seconds is minimum for the timeout
//
sm_dwHandshakeTimeoutInSec = MINIMUM_RENEGOTIATION_TIMEOUT_IN_SEC;
}
}
RegCloseKey( hKeyParam );
}
//
// Get a UL handle to the RawStreamPool (or whatever)
//
ulRet = HttpCreateFilter( &_hFilterHandle,
_pwszFilterChannelName,
NULL,
0 );
if ( ulRet != ERROR_SUCCESS )
{
//
// HTTPFilter service may have created Filter already
// try just open it then
//
ulRet = HttpOpenFilter( &_hFilterHandle,
_pwszFilterChannelName,
0 );
if ( ulRet != ERROR_SUCCESS )
{
hr = HRESULT_FROM_WIN32( ulRet );
goto Finished;
}
}
//
// Create private thread pool for strmfilt
//
THREAD_POOL_CONFIG ThreadPoolConfig;
SYSTEM_INFO si;
//
// we will use GetSystemInfo to retrieve number of processors
//
GetSystemInfo( &si );
hr = InitializeThreadPoolConfigWithDefaults( &ThreadPoolConfig );
if (FAILED(hr))
{
goto Finished;
}
//
// Manual override of some parameters
//
// The time (in msecs) of how long the threads can stay alive with no activity
ThreadPoolConfig.dwThreadTimeout =
STRMFILT_THREAD_POOL_DEF_THREAD_TIMEOUT * 1000;
// The number of threads to start
ThreadPoolConfig.dwInitialThreadCount =
STRMFILT_THREAD_POOL_DEF_THREAD_COUNT;
ThreadPoolConfig.dwInitialStackSize =
IIS_DEFAULT_INITIAL_STACK_SIZE;
// There is not much blocking on FILTER_CHANNEL execution
// Performance tests suggested that setting #threads to match
// # processors is the right thing to do
// Note: There are cases where soft thread limit is bumped up
// such as before AcceptSecurityContext() when hardware
// accelerator is used
//
// we need miminum of 2 threads because current thread pool manager implementation
// will not allow to add new threads when soft thread limit is incremented
//
ThreadPoolConfig.dwSoftLimitThreadCount = max( 2, si.dwNumberOfProcessors );
//
// Override threadpool settings with registry values (if configured)
//
hr = OverrideThreadPoolConfigWithRegistry( &ThreadPoolConfig,
REGISTRY_KEY_HTTPFILTER_PARAMETERS_W );
if (FAILED(hr))
{
goto Finished;
}
fRet = THREAD_POOL::CreateThreadPool( &_pThreadPool,
&ThreadPoolConfig );
if ( !fRet )
{
DBGPRINTF(( DBG_CONTEXT,
"Failed to create ThreadPool for Strmfilt\n" ));
hr = E_FAIL;
goto Finished;
}
DBG_ASSERT( _pThreadPool != NULL );
//
// Associate a completion routine with the thread pool
//
DBG_ASSERT( _hFilterHandle != INVALID_HANDLE_VALUE );
DBG_ASSERT( _hFilterHandle != NULL );
if ( !_pThreadPool->BindIoCompletionCallback(_hFilterHandle,
OverlappedCompletionRoutine,
0 ) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto Finished;
}
//
// Keep a set number of filter accepts outstanding
//
_cDesiredOutstanding = sm_dwContextDesiredOutstanding;
//
// If sm_dwHandshakeTimeoutInSec == 0 then timers are disabled
//
if ( sm_dwHandshakeTimeoutInSec != 0 )
{
fRet = CreateTimerQueueTimer(
&_hTimer,
NULL,
FILTER_CHANNEL::TimerCallback,
this,
(( sm_dwHandshakeTimeoutInSec / 2 )) * 1000,
(( sm_dwHandshakeTimeoutInSec / 2 )) * 1000,
WT_EXECUTELONGFUNCTION );
if ( !fRet )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto Finished;
}
}
Finished:
if ( FAILED( hr ) )
{
FILTER_CHANNEL::Terminate();
}
return hr;
}
VOID
FILTER_CHANNEL::Terminate(
VOID
)
/*++
Routine Description:
Global termination
Arguments:
None
Return Value:
None
--*/
{
//
// Thread pool must be terminated before any other objects get cleaned up
// to assure that there are no worker threads still executing
//
if ( _hTimer != NULL )
{
DeleteTimerQueueTimer(
NULL, //use default timer queue
_hTimer,
INVALID_HANDLE_VALUE /*wait for completion of the callback*/);
_hTimer = NULL;
}
if ( _pThreadPool != NULL )
{
_pThreadPool->TerminateThreadPool();
_pThreadPool = NULL;
}
if ( _hFilterHandle != NULL )
{
CloseHandle( _hFilterHandle );
_hFilterHandle = NULL;
}
if ( _pTraceLog != NULL )
{
DestroyRefTraceLog( _pTraceLog );
_pTraceLog = NULL;
}
for ( int i = 0; i < _dwInitcsFiltChannelContexts; i ++ )
{
DeleteCriticalSection( &_csFiltChannelContexts[ i ] );
}
_dwInitcsFiltChannelContexts = 0;
};
VOID
FILTER_CHANNEL::InsertFiltChannelContext(
FILTER_CHANNEL_CONTEXT *pFiltChannelContext
)
{
DWORD dwFilterContextCount = (DWORD)
InterlockedIncrement( (PLONG) &_dwTotalFilterChannelContexts );
//
// Determine which lock, sublist will be used for this context
// Use round robin
//
DWORD dwLockIndex = dwFilterContextCount % NUM_CS_FILT_CHANNEL_CONTEXTS;
pFiltChannelContext->SetLockIndex( dwLockIndex );
EnterCriticalSection( &_csFiltChannelContexts[ dwLockIndex ] );
InsertHeadList( &_ListHead[ dwLockIndex ], &pFiltChannelContext->_ListEntry );
//
// We have to interlocked increment the _cFiltChannelContexts
// because the the lock we acquired
// is only the lock to a sublist of contexts so there may be
// multiple concurrent attempts to increment _cFiltChannelContexts
//
InterlockedIncrement( (LONG *)&_cFiltChannelContexts );
LeaveCriticalSection( &_csFiltChannelContexts[ dwLockIndex ] );
}
VOID
FILTER_CHANNEL::RemoveFiltChannelContext(
FILTER_CHANNEL_CONTEXT *pFiltChannelContext
)
{
DWORD dwLockIndex = pFiltChannelContext->QueryLockIndex();
EnterCriticalSection( &_csFiltChannelContexts[ dwLockIndex ] );
InterlockedDecrement( (LONG *)&_cFiltChannelContexts );
RemoveEntryList( &pFiltChannelContext->_ListEntry );
LeaveCriticalSection( &_csFiltChannelContexts[ dwLockIndex ] );
}
VOID
FILTER_CHANNEL::InsertFiltChannelContextToTimerList(
FILTER_CHANNEL_CONTEXT *pFiltChannelContext
)
/*++
Routine Description:
Add context to the timeout timer list
Arguments:
pFiltChannelContext - context to be added to the timeout timer list
Return Value:
None
--*/
{
DWORD dwCurrentTickCount = GetTickCount();
if ( sm_dwHandshakeTimeoutInSec == 0 )
{
//
// timeouts are disabled
//
return;
}
DWORD dwLockIndex = pFiltChannelContext->QueryLockIndex();
EnterCriticalSection( &_csFiltChannelContexts[ dwLockIndex ] );
if ( pFiltChannelContext->QueryIsAlreadyOnTimerList() )
{
// Entry already on the list but
// tickCount for the entry will be updated
// We have to remove entry from the current position
// so that it can be added to the end since
// the list is ordered by the TickCounts
//
RemoveEntryList( &pFiltChannelContext->_TimerListEntry );
}
//
// New entries go to the end of the list
//
InsertTailList( &_TimerListHead[ dwLockIndex ], &pFiltChannelContext->_TimerListEntry );
pFiltChannelContext->SetTimerTickCount( dwCurrentTickCount );
LeaveCriticalSection( &_csFiltChannelContexts[ dwLockIndex ] );
}
VOID
FILTER_CHANNEL::RemoveFiltChannelContextFromTimerList(
FILTER_CHANNEL_CONTEXT *pFiltChannelContext
)
{
/*++
Routine Description:
Remove context from the timeout timer list
Arguments:
pFiltChannelContext - context to be removed from the timeout timer list
Return Value:
None
--*/
DWORD dwLockIndex = pFiltChannelContext->QueryLockIndex();
EnterCriticalSection( &_csFiltChannelContexts[ dwLockIndex ] );
RemoveEntryList( &pFiltChannelContext->_TimerListEntry );
pFiltChannelContext->ResetTimerTickCount();
LeaveCriticalSection( &_csFiltChannelContexts[ dwLockIndex ] );
}
VOID
FILTER_CHANNEL::WaitForContextDrain(
VOID
)
/*++
Routine Description:
Wait for all contexts to go away
Arguments:
None
Return Value:
None
--*/
{
while ( _cFiltChannelContexts != 0 )
{
DBGPRINTF(( DBG_CONTEXT,
"Waiting for %d CONTEXTs for %S to drain\n",
_cFiltChannelContexts,
_pwszFilterChannelName ));
Sleep( 1000 );
}
//
// there should be no outstanding contexts left
//
DBG_ASSERT( _cOutstandingContexts == 0 );
}
HRESULT
FILTER_CHANNEL::StartListening(
VOID
)
/*++
Routine Description:
Start listening - create initial UL_CONTEXTs
Arguments:
None
Return Value:
HRESULT
--*/
{
HRESULT hr = ManageOutstandingContexts();
if ( SUCCEEDED( hr ) )
{
InterlockedExchange( &_lStartedListening, 1 );
}
return hr;
}
VOID
FILTER_CHANNEL::StopListening(
VOID
)
/*++
Routine Description:
Stop listening and wait for contexts to drain
Arguments:
None
Return Value:
None
--*/
{
DBG_REQUIRE( HttpShutdownFilter( _hFilterHandle ) == ERROR_SUCCESS );
InterlockedExchange(&_lStartedListening, 0 );
WaitForContextDrain();
}
//
// Keep enough UL_CONTEXTs listening
//
HRESULT
FILTER_CHANNEL::ManageOutstandingContexts(
VOID
)
/*++
Routine Description:
add sufficient number of new contexts
Arguments:
None
Return Value:
HRESULT
--*/
{
LONG cRequired;
FILTER_CHANNEL_CONTEXT * pContext;
HRESULT hr = NO_ERROR;
if ( _cOutstandingContexts < _cDesiredOutstanding )
{
cRequired = _cDesiredOutstanding - _cOutstandingContexts;
//
// Make sure the value is not negative
//
cRequired = max( 0, cRequired );
for ( LONG i = 0; i < cRequired; i++ )
{
hr = CreateContext( &pContext );
if ( FAILED( hr ) )
{
break;
}
DBG_ASSERT( pContext );
hr = pContext->DoAccept();
if ( FAILED( hr ) )
{
pContext->DereferenceFiltChannelContext();
break;
}
}
}
return hr;
}
HRESULT
FILTER_CHANNEL::DecrementAndManageOutstandingContexts(
VOID
)
/*++
Routine Description:
Decrement available outstanding Context and
add sufficient number of new contexts
Arguments:
None
Return Value:
HRESULT
--*/
{
HRESULT hr = E_FAIL;
LONG lPreviousContexts =
InterlockedDecrement( &_cOutstandingContexts );
if ( _lStartedListening == 1 )
{
hr = ManageOutstandingContexts();
if ( FAILED(hr) &&
lPreviousContexts == 1 )
{
//
// If there are no outstanding contexts
// strmfilt will not be able to accept new connections
// Keep retrying until we succeed
// or until service get's shut down
//
// Note: this loop will be blocking processing
// the completion of the new connection but if
// we hit problems with adding new outstanding context
// that is big enough problem so we can afford
// to sacrifise this one connection until
// adding of new outstanding contexts succeeds
LONG lOldValue = InterlockedExchange(
&_lEnteredOutstandingContextsAddingLoop,
1 );
//
// Let's have only one thread trying to add
// outstanding contexts to minimize impact on system
// (in low memory or similar condition - under normal
// circumstances contexts should have no problems to be added)
//
if ( lOldValue == 0 )
{
//
// if lOldValue = 0 it means we are the first thread trying
//
do
{
Sleep( 2000 );
hr = ManageOutstandingContexts();
}while( FAILED( hr ) && _lStartedListening == 1 );
InterlockedExchange(
&_lEnteredOutstandingContextsAddingLoop,
0 );
}
}
}
return hr;
}
HRESULT
FILTER_CHANNEL::EnableISAPIFilters(
ISAPI_FILTERS_CALLBACKS * pConfig
)
/*++
Routine Description:
enable ISAPI Filters
( all new connections will notify ISAPI Filters )
Arguments:
pStreamConfig - config info
Return Value:
HRESULT
--*/
{
HRESULT hr = E_FAIL;
hr = ISAPI_STREAM_CONTEXT::EnableISAPIFilters( pConfig );
if ( FAILED( hr ) )
{
return hr;
}
SetNotifyISAPIFilters( TRUE );
return S_OK;
}
VOID
FILTER_CHANNEL::DisableISAPIFilters(
VOID
)
/*++
Routine Description:
safely terminate ISAPI Filters
( all connections with ISAPI Filters must be closed )
Arguments:
None
Return Value:
None
--*/
{
//
// stop notifying ISAPI Filters
//
SetNotifyISAPIFilters( FALSE );
ISAPI_STREAM_CONTEXT::DisableISAPIFilters();
}
//static
VOID
WINAPI
FILTER_CHANNEL::TimerCallback(
PVOID pParam,
BOOLEAN
)
/*++
Routine Description:
Close all the connections that have timed out based on the
timer settings
Arguments:
None
Return Value:
None
--*/
{
//
// slTimerCallbackEntryCount will guard against the case
// where new timer callback comes in when the previous is still
// executing
//
static LONG slTimerCallbackEntryCount = 0;
if ( InterlockedIncrement (&slTimerCallbackEntryCount ) != 1 )
{
InterlockedDecrement ( &slTimerCallbackEntryCount );
return;
}
DWORD dwCurrentTickCount = GetTickCount();
DBG_ASSERT( pParam != NULL );
FILTER_CHANNEL * pFilterChannel = reinterpret_cast<FILTER_CHANNEL *>( pParam );
// Filter channel Context lock is split to minimize scaling problems
//
for ( int dwLockIndex = 0; dwLockIndex < NUM_CS_FILT_CHANNEL_CONTEXTS;
dwLockIndex ++ )
{
EnterCriticalSection( &pFilterChannel->_csFiltChannelContexts[ dwLockIndex ] );
while ( !IsListEmpty ( &pFilterChannel->_TimerListHead [ dwLockIndex ] ) )
{
LIST_ENTRY *pCurrentEntry =
pFilterChannel->_TimerListHead[ dwLockIndex ].Flink;
FILTER_CHANNEL_CONTEXT * pFiltChannelContext =
CONTAINING_RECORD( pCurrentEntry,
FILTER_CHANNEL_CONTEXT,
_TimerListEntry );
DBG_ASSERT( pFiltChannelContext->CheckSignature() );
DWORD dwElapsedTimeInMSec;
if ( dwCurrentTickCount < pFiltChannelContext->QueryTimerTickCount() )
{
//
// Wrap around
//
dwElapsedTimeInMSec = MAXDWORD - dwCurrentTickCount +
pFiltChannelContext->QueryTimerTickCount();
}
else
{
dwElapsedTimeInMSec = dwCurrentTickCount -
pFiltChannelContext->QueryTimerTickCount();
}
if ( dwElapsedTimeInMSec < sm_dwHandshakeTimeoutInSec * 1000 )
{
//
// all the subsequent entries are newer the the current one
// so we can safely assume that neither of them needs to
// expire at this point
//
break;
}
//
// It is time to request closing the connection
//
pFilterChannel->RemoveFiltChannelContextFromTimerList(
pFiltChannelContext );
//
// Note: Do not use pFilterChannelContext passed the StartClose()
// because it will not be guaranteed to be around any more
//
// Note2: StartClose() may reentrantly acquire the
// pFilterChannel->_csFiltChannelContexts lock if destructor
// happens to be called on FiltChannelContext dereference
// inside the StartClose()
pFiltChannelContext->StartClose();
}
LeaveCriticalSection( &pFilterChannel->_csFiltChannelContexts[ dwLockIndex ] );
}
InterlockedDecrement ( &slTimerCallbackEntryCount );
}
SSL_SERVER_FILTER_CHANNEL_CONTEXT::SSL_SERVER_FILTER_CHANNEL_CONTEXT(
SSL_SERVER_FILTER_CHANNEL *pManager
)
: FILTER_CHANNEL_CONTEXT(pManager)
{
_dwSignature = SSL_SERVER_FILTER_CHANNEL_CONTEXT_SIGNATURE;
}
SSL_SERVER_FILTER_CHANNEL_CONTEXT::~SSL_SERVER_FILTER_CHANNEL_CONTEXT()
{
_dwSignature = SSL_SERVER_FILTER_CHANNEL_CONTEXT_SIGNATURE_FREE;
}
//static
HRESULT
SSL_SERVER_FILTER_CHANNEL_CONTEXT::Initialize(
VOID
)
/*++
Routine Description:
Global initialization routine for FILTER_CHANNEL_CONTEXT
Arguments:
None
Return Value:
HRESULT
--*/
{
ALLOC_CACHE_CONFIGURATION acConfig;
//
// Setup allocation lookaside
//
acConfig.nConcurrency = 1;
acConfig.nThreshold = 100;
acConfig.cbSize = sizeof( SSL_SERVER_FILTER_CHANNEL_CONTEXT );
DBG_ASSERT( sm_pachFilterChannelContexts == NULL );
sm_pachFilterChannelContexts = new ALLOC_CACHE_HANDLER( "SSL_SERVER_FILTER_CHANNEL_CONTEXT",
&acConfig );
if ( sm_pachFilterChannelContexts == NULL )
{
return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
return NO_ERROR;
}
//static
VOID
SSL_SERVER_FILTER_CHANNEL_CONTEXT::Terminate(
VOID
)
/*++
Routine Description:
Terminate FILTER_CHANNEL_CONTEXT globals
Arguments:
None
Return Value:
None
--*/
{
if ( sm_pachFilterChannelContexts != NULL )
{
delete sm_pachFilterChannelContexts;
sm_pachFilterChannelContexts = NULL;
}
}
HRESULT
SSL_SERVER_FILTER_CHANNEL_CONTEXT::Create(
VOID
)
/*++
Routine Description:
Initialize a SSL_SERVER_FILTER_CHANNEL_CONTEXT
Arguments:
None
Return Value:
HRESULT
--*/
{
DBG_ASSERT( _pSSLContext == NULL );
DBG_ASSERT( _pISAPIContext == NULL );
_pSSLContext = new SSL_STREAM_CONTEXT( this );
if ( _pSSLContext == NULL )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
return NO_ERROR;
}
VOID
OverlappedCompletionRoutine(
DWORD dwErrorCode,
DWORD dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped
)
/*++
Routine Description:
Magic completion routine
Arguments:
None
Return Value:
None
--*/
{
UL_OVERLAPPED_CONTEXT * pContextOverlapped = NULL;
HRESULT hr;
FILTER_CHANNEL_CONTEXT * pFiltChannelContext = NULL;
DBG_ASSERT( lpOverlapped != NULL );
pContextOverlapped =
UL_OVERLAPPED_CONTEXT::GetUlOverlappedContext( lpOverlapped );
DBG_ASSERT( pContextOverlapped != NULL );
//
// pFiltChannelContext was referenced in the Async Read/Write call
// Reference is still held and it is safe to access the object
// Completion routines will release the reference
//
pFiltChannelContext = pContextOverlapped->QueryContext();
//
// Call the appropriate completion routine
//
//
// Note: Completion routines may delete UL_OVERLAPPED_CONTEXT
// at least OnRawWriteCompletion() deletes temporary contexts
// Do not use pContextOverlapped after CompletionRoutines were called
//
switch( pContextOverlapped->QueryType() )
{
case UL_OVERLAPPED_CONTEXT_RAW_READ:
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"ENTER OnRawReadCompletion( bytes:%d, dwErr:%d )\n",
dwNumberOfBytesTransfered,
dwErrorCode
));
}
hr = pFiltChannelContext->OnRawReadCompletion( dwNumberOfBytesTransfered,
dwErrorCode );
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"LEAVE OnRawReadCompletion( bytes:%d, dwErr:%d ) hr=0x%x\n",
dwNumberOfBytesTransfered,
dwErrorCode,
hr
));
}
break;
case UL_OVERLAPPED_CONTEXT_RAW_WRITE:
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"ENTER OnRawWriteCompletion( bytes:%d, dwErr:%d )\n",
dwNumberOfBytesTransfered,
dwErrorCode
));
}
hr = pFiltChannelContext->OnRawWriteCompletion( dwNumberOfBytesTransfered,
dwErrorCode,
pContextOverlapped );
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"LEAVE OnRawWriteCompletion( bytes:%d, dwErr:%d ) hr=0x%x\n",
dwNumberOfBytesTransfered,
dwErrorCode,
hr
));
}
break;
case UL_OVERLAPPED_CONTEXT_APP_READ:
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"ENTER OnAppReadCompletion( bytes:%d, dwErr:%d )\n",
dwNumberOfBytesTransfered,
dwErrorCode
));
}
hr = pFiltChannelContext->OnAppReadCompletion( dwNumberOfBytesTransfered,
dwErrorCode );
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"LEAVE OnAppReadCompletion ( bytes:%d, dwErr:%d ) hr=0x%x\n",
dwNumberOfBytesTransfered,
dwErrorCode,
hr
));
}
break;
case UL_OVERLAPPED_CONTEXT_APP_WRITE:
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"ENTER OnAppWriteCompletion( bytes:%d, dwErr:%d )\n",
dwNumberOfBytesTransfered,
dwErrorCode
));
}
hr = pFiltChannelContext->OnAppWriteCompletion( dwNumberOfBytesTransfered,
dwErrorCode );
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"LEAVE OnAppWriteCompletion( bytes:%d, dwErr:%d ) hr=0x%x\n",
dwNumberOfBytesTransfered,
dwErrorCode,
hr
));
}
break;
case UL_OVERLAPPED_CONTEXT_CLOSE:
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"Connection close completed (there still may be outstanding I/Os unless the refcount is 1)\n"
));
}
//
// Closing the connection was completed
// Notify whoever is interested
//
pFiltChannelContext->CloseNotify();
//
// Do the final dereference
//
pFiltChannelContext->DereferenceFiltChannelContext();
break;
default:
hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
DBG_ASSERT( FALSE );
}
pFiltChannelContext = NULL;
// Reference on the context was released in completion routine
// don't use it past this point
}