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
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
|
|
}
|