2025-04-27 07:49:33 -04:00

1736 lines
41 KiB
C++

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name :
ulcontext.cxx
Abstract:
Implementation of UL_CONTEXT. One such object for every connection
Author:
Bilal Alam (BAlam) 29-March-2000
Environment:
Win32 - User Mode
Project:
Stream Filter Worker Process
--*/
#include "precomp.hxx"
#define DEFAULT_RAW_READ_SIZE 32768
#define DEFAULT_APP_READ_SIZE 32768
LIST_ENTRY UL_CONTEXT::sm_ListHead;
CRITICAL_SECTION UL_CONTEXT::sm_csUlContexts;
DWORD UL_CONTEXT::sm_cUlContexts;
HANDLE UL_CONTEXT::sm_hFilterHandle = NULL;
THREAD_POOL * UL_CONTEXT::sm_pThreadPool = NULL;
LONG UL_CONTEXT::sm_cDesiredOutstanding;
LONG UL_CONTEXT::sm_cOutstandingContexts;
PTRACE_LOG UL_CONTEXT::sm_pTraceLog;
UL_CONTEXT::UL_CONTEXT()
: _RawWriteOverlapped( OVERLAPPED_CONTEXT_RAW_WRITE ),
_RawReadOverlapped( OVERLAPPED_CONTEXT_RAW_READ ),
_AppWriteOverlapped( OVERLAPPED_CONTEXT_APP_WRITE ),
_AppReadOverlapped( OVERLAPPED_CONTEXT_APP_READ ),
_buffConnectionInfo( _abConnectionInfo, sizeof( _abConnectionInfo ) ),
_fCloseConnection( FALSE ),
_cRefs( 1 ),
_cbReadData( 0 ),
_fNewConnection( TRUE ),
_pSSLContext( NULL ),
#ifdef ISAPI
_pISAPIContext( NULL ),
#endif
_cbNextRawReadSize( DEFAULT_RAW_READ_SIZE ),
_ulFilterBufferType( (HTTP_FILTER_BUFFER_TYPE) -1 )
{
_RawWriteOverlapped.SetContext( this );
_RawReadOverlapped.SetContext( this );
_AppWriteOverlapped.SetContext( this );
_AppReadOverlapped.SetContext( this );
EnterCriticalSection( &sm_csUlContexts );
InsertHeadList( &sm_ListHead, &_ListEntry );
sm_cUlContexts++;
LeaveCriticalSection( &sm_csUlContexts );
_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);
_dwSignature = UL_CONTEXT_SIGNATURE;
}
UL_CONTEXT::~UL_CONTEXT()
{
_dwSignature = UL_CONTEXT_SIGNATURE_FREE;
//
// Cleanup any attached stream context
//
#ifdef ISAPI
if ( _pISAPIContext != NULL )
{
delete _pISAPIContext;
_pISAPIContext = NULL;
}
#endif
if ( _pSSLContext != NULL )
{
delete _pSSLContext;
_pSSLContext = NULL;
}
//
// Manage the list of active UL_CONTEXTs
//
EnterCriticalSection( &sm_csUlContexts );
sm_cUlContexts--;
RemoveEntryList( &_ListEntry );
LeaveCriticalSection( &sm_csUlContexts );
}
VOID
UL_CONTEXT::ReferenceUlContext(
VOID
)
/*++
Routine Description:
Reference the UL_CONTEXT
Arguments:
none
Return Value:
none
--*/
{
LONG cRefs;
cRefs = InterlockedIncrement( &_cRefs );
//
// Log the reference ( sm_pTraceLog!=NULL if DBG=1)
//
if ( sm_pTraceLog != NULL )
{
WriteRefTraceLog( sm_pTraceLog,
cRefs,
this );
}
}
VOID
UL_CONTEXT::DereferenceUlContext(
VOID
)
/*++
Routine Description:
Dereference (and possible destroy) the UL_CONTEXT
Arguments:
none
Return Value:
none
--*/
{
LONG cRefs;
cRefs = InterlockedDecrement( &_cRefs );
if ( sm_pTraceLog != NULL )
{
WriteRefTraceLog( sm_pTraceLog,
cRefs,
this );
}
if ( cRefs == 0 )
{
delete this;
}
}
HRESULT
UL_CONTEXT::OnAppReadCompletion(
DWORD cbCompletion,
DWORD dwCompletionStatus
)
/*++
Routine Description:
Completion for reads from an application
Arguments:
cbCompletion - Bytes of completion
dwCompletionStatus - Completion error
Return Value:
HRESULT
--*/
{
HTTP_FILTER_BUFFER * pFilterBuffer;
HRESULT hr;
RAW_STREAM_INFO rawStreamInfo;
BOOL fComplete = FALSE;
//
// Just bail on errors
//
if ( dwCompletionStatus != NO_ERROR )
{
return HRESULT_FROM_WIN32( dwCompletionStatus );
}
pFilterBuffer = (HTTP_FILTER_BUFFER *) _ulFilterBuffer.pBuffer;
_ulFilterBufferType = pFilterBuffer->BufferType;
DBG_ASSERT( !_fNewConnection );
//
// If UL is telling us to close the connection, then do so now
//
if ( _ulFilterBufferType == HttpFilterBufferCloseConnection )
{
StartClose();
return NO_ERROR;
}
//
// Setup raw stream descriptor
//
rawStreamInfo.pbBuffer = (PBYTE) pFilterBuffer->pBuffer;
rawStreamInfo.cbBuffer = pFilterBuffer->BufferSize;
rawStreamInfo.cbData = pFilterBuffer->BufferSize;
#ifdef ISAPI
//
// First notify ISAPI filters if this is a stream from the application
//
DBG_ASSERT( g_pStreamFilter != NULL );
if ( _ulFilterBufferType == HttpFilterBufferHttpStream &&
g_pStreamFilter->QueryNotifyISAPIFilters() )
{
DBG_ASSERT( _pISAPIContext != NULL );
hr = _pISAPIContext->ProcessRawWriteData( &rawStreamInfo,
&fComplete );
if ( FAILED( hr ) )
{
return hr;
}
if ( fComplete )
{
StartClose();
return NO_ERROR;
}
}
#endif
//
// Next notify SSL filter always
//
DBG_ASSERT( _pSSLContext != NULL );
hr = _pSSLContext->ProcessRawWriteData( &rawStreamInfo,
&fComplete );
if ( FAILED( hr ) )
{
return hr;
}
if ( fComplete )
{
StartClose();
return NO_ERROR;
}
//
// 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
//
hr = DoRawWrite( UL_CONTEXT_FLAG_ASYNC,
rawStreamInfo.pbBuffer,
rawStreamInfo.cbData,
NULL );
if ( FAILED( hr ) )
{
return hr;
}
//
// New app read will be kicked off in OnRawWriteCompletion
//
}
else
{
//
// Kick off another app read
//
_ulFilterBuffer.pBuffer = (PBYTE) _buffAppReadData.QueryPtr();
_ulFilterBuffer.BufferSize = _buffAppReadData.QuerySize();
return DoAppRead( UL_CONTEXT_FLAG_ASYNC,
&_ulFilterBuffer,
NULL );
}
return NO_ERROR;
}
HRESULT
UL_CONTEXT::OnRawReadCompletion(
DWORD cbCompletion,
DWORD dwCompletionStatus
)
/*++
Routine Description:
Get read completions off the wire. This includes the initial
completion for the UlFilterAccept()
Arguments:
cbCompletion - Bytes of completion
dwCompletionStatus - Completion error
Return Value:
HRESULT
--*/
{
HTTP_NETWORK_ADDRESS_IPV4 * pLocalAddress;
HTTP_NETWORK_ADDRESS_IPV4 * pRemoteAddress;
HRESULT hr;
BOOL fReadMore = FALSE;
BOOL fComplete = FALSE;
HTTP_FILTER_BUFFER ulFilterBuffer;
DWORD cbWritten;
RAW_STREAM_INFO rawStreamInfo;
//
// Handle errors
//
if ( dwCompletionStatus != NO_ERROR )
{
if ( _fNewConnection )
{
InterlockedDecrement( &sm_cOutstandingContexts );
}
return HRESULT_FROM_WIN32( dwCompletionStatus );
}
//
// If this is a new connection, then grok connection information, and
// maintain pending count
//
if ( _fNewConnection )
{
_fNewConnection = FALSE;
//
// This is a new connection. We have one less UL_CONTEXT to
// listen for incoming requests. Correct that if necessary.
//
InterlockedDecrement( &sm_cOutstandingContexts );
ManageOutstandingContexts();
//
// Convert the UL addresses into something nicer!
//
HTTP_TRANSPORT_ADDRESS *pAddress = &_pConnectionInfo->Address;
DBG_ASSERT( pAddress->LocalAddressType == HTTP_NETWORK_ADDRESS_TYPE_IPV4 );
pLocalAddress = (HTTP_NETWORK_ADDRESS_IPV4 *)pAddress->pLocalAddress;
_connectionContext.LocalAddress = pLocalAddress->IpAddress;
_connectionContext.LocalPort = pLocalAddress->Port;
DBG_ASSERT( pAddress->RemoteAddressType == HTTP_NETWORK_ADDRESS_TYPE_IPV4 );
pRemoteAddress = (HTTP_NETWORK_ADDRESS_IPV4 *) pAddress->pRemoteAddress;
_connectionContext.RemoteAddress = pRemoteAddress->IpAddress;
_connectionContext.RemotePort = pRemoteAddress->Port;
_connectionContext.fIsSecure = FALSE;
_connectionContext.RawConnectionId = _pConnectionInfo->ConnectionId;
#ifdef ISAPI
_connectionContext.pfnSendDataBack = ISAPI_STREAM_CONTEXT::SendDataBack;
#endif
_connectionContext.pvStreamContext = this;
//
// copy out the server name.
//
_connectionContext.ServerNameLength =
_pConnectionInfo->ServerNameLength;
_connectionContext.pServerName = _pConnectionInfo->pServerName;
//
// Fill in our read buffer (as if we had read it in directly)
//
_cbReadData = _pConnectionInfo->InitialDataSize;
if ( !_buffReadData.Resize( max( _pConnectionInfo->InitialDataSize,
QueryNextRawReadSize() ) ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
memcpy( _buffReadData.QueryPtr(),
_pConnectionInfo->pInitialData,
_pConnectionInfo->InitialDataSize );
//
// First indicate a new connection
//
DBG_ASSERT( _pSSLContext != NULL );
hr = _pSSLContext->ProcessNewConnection( &_connectionContext );
if ( FAILED( hr ) )
{
return hr;
}
#ifdef ISAPI
if ( g_pStreamFilter->QueryNotifyISAPIFilters() )
{
DBG_ASSERT( _pISAPIContext != NULL );
hr = _pISAPIContext->ProcessNewConnection( &_connectionContext );
if ( FAILED( hr ) )
{
return hr;
}
}
#endif
//
// Kick off an app read now.
//
if ( !_buffAppReadData.Resize( DEFAULT_APP_READ_SIZE ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
_ulFilterBuffer.pBuffer = (PBYTE) _buffAppReadData.QueryPtr();
_ulFilterBuffer.BufferSize = _buffAppReadData.QuerySize();
hr = DoAppRead( UL_CONTEXT_FLAG_ASYNC,
&_ulFilterBuffer,
NULL );
if ( FAILED( hr ) )
{
return hr;
}
}
else
{
_cbReadData += cbCompletion;
}
//
// reset default raw read size
//
SetNextRawReadSize( DEFAULT_RAW_READ_SIZE );
rawStreamInfo.pbBuffer = (PBYTE) _buffReadData.QueryPtr();
rawStreamInfo.cbBuffer = _buffReadData.QuerySize();
rawStreamInfo.cbData = _cbReadData;
//
// First, we will notify SSL
//
DBG_ASSERT( _pSSLContext != NULL );
hr = _pSSLContext->ProcessRawReadData( &rawStreamInfo,
&fReadMore,
&fComplete );
if ( FAILED( hr ) )
{
return hr;
}
_cbReadData = 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 _buffReadData
//
if ( rawStreamInfo.pbBuffer != _buffReadData.QueryPtr() )
{
DBG_ASSERT( rawStreamInfo.cbData <= _buffReadData.QuerySize() );
memmove( _buffReadData.QueryPtr(),
rawStreamInfo.pbBuffer,
rawStreamInfo.cbData
);
}
if ( !_buffReadData.Resize( rawStreamInfo.cbData +
QueryNextRawReadSize() ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
return DoRawRead( UL_CONTEXT_FLAG_ASYNC,
(PBYTE) _buffReadData.QueryPtr() + _cbReadData,
QueryNextRawReadSize(),
NULL );
}
if ( fComplete )
{
StartClose();
return NO_ERROR;
}
//
// Reset the next read size before calling into filters since SSL may
// have done a really small read, just previous to this.
//
SetNextRawReadSize( DEFAULT_RAW_READ_SIZE );
#ifdef ISAPI
//
// Now we can start notifying ISAPI filters if needed (and there is
// data to process)
//
DBG_ASSERT( g_pStreamFilter != NULL );
if ( g_pStreamFilter->QueryNotifyISAPIFilters() )
{
fComplete = FALSE;
fReadMore = FALSE;
DBG_ASSERT( _pISAPIContext != NULL );
hr = _pISAPIContext->ProcessRawReadData( &rawStreamInfo,
&fReadMore,
&fComplete );
if ( FAILED( hr ) )
{
return hr;
}
_cbReadData = 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 _buffReadData
//
if ( rawStreamInfo.pbBuffer != _buffReadData.QueryPtr() )
{
DBG_ASSERT( rawStreamInfo.cbData <= _buffReadData.QuerySize() );
memmove( _buffReadData.QueryPtr(),
rawStreamInfo.pbBuffer,
rawStreamInfo.cbData
);
}
if ( !_buffReadData.Resize( rawStreamInfo.cbData +
QueryNextRawReadSize() ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
return DoRawRead( UL_CONTEXT_FLAG_ASYNC,
(PBYTE) _buffReadData.QueryPtr() + _cbReadData,
QueryNextRawReadSize(),
NULL );
}
if ( fComplete )
{
StartClose();
return NO_ERROR;
}
}
#endif
//
// 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
//
_cbReadData = 0;
if ( rawStreamInfo.cbData != 0 )
{
ulFilterBuffer.BufferType = HttpFilterBufferHttpStream;
ulFilterBuffer.BufferSize = rawStreamInfo.cbData;
ulFilterBuffer.pBuffer = rawStreamInfo.pbBuffer;
hr = DoAppWrite( UL_CONTEXT_FLAG_ASYNC,
&ulFilterBuffer,
&cbWritten );
if ( FAILED( hr ) )
{
return hr;
}
//
// new raw read will be kicked off in OnAppWriteCompletion()
//
}
else
{
//
// Kick off another raw read
//
//
// reset default raw read size
//
SetNextRawReadSize( DEFAULT_RAW_READ_SIZE );
if ( !_buffReadData.Resize( QueryNextRawReadSize() ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
hr = DoRawRead( UL_CONTEXT_FLAG_ASYNC,
_buffReadData.QueryPtr(),
QueryNextRawReadSize() ,
NULL );
if ( FAILED( hr ) )
{
return hr;
}
}
return NO_ERROR;
}
HRESULT
UL_CONTEXT::OnRawWriteCompletion(
DWORD cbCompletion,
DWORD dwCompletionStatus
)
/*++
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
dwCompletionStatus - Completion error
Return Value:
HRESULT
--*/
{
if ( dwCompletionStatus != NO_ERROR )
{
return HRESULT_FROM_WIN32( dwCompletionStatus );
}
//
// Kick off another app read
//
_ulFilterBuffer.pBuffer = (PBYTE) _buffAppReadData.QueryPtr();
_ulFilterBuffer.BufferSize = _buffAppReadData.QuerySize();
return DoAppRead( UL_CONTEXT_FLAG_ASYNC,
&_ulFilterBuffer,
NULL );
}
HRESULT
UL_CONTEXT::OnAppWriteCompletion(
DWORD cbCompletion,
DWORD dwCompletionStatus
)
/*++
Routine Description:
after app write completes we need to make another RawRead that is
the source of data for another asynchronous AppWrite.
Note: AppWrite should be called asynchronously only from OnRawReadCompletion()
Otherwise it would be necessary to change logic of this function
Arguments:
cbCompletion - Bytes of completion
dwCompletionStatus - Completion error
Return Value:
HRESULT
--*/
{
HRESULT hr = E_FAIL;
if ( dwCompletionStatus != NO_ERROR )
{
return HRESULT_FROM_WIN32( dwCompletionStatus );
}
//
// Kick off another raw read
//
//
// reset default raw read size
//
SetNextRawReadSize( DEFAULT_RAW_READ_SIZE );
if ( !_buffReadData.Resize( QueryNextRawReadSize() ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
hr = DoRawRead( UL_CONTEXT_FLAG_ASYNC,
_buffReadData.QueryPtr(),
QueryNextRawReadSize() ,
NULL );
return hr;
}
HRESULT
UL_CONTEXT::DoRawWrite(
DWORD dwFlags,
PVOID pvBuffer,
DWORD cbBuffer,
DWORD * pcbWritten
)
/*++
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
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 DoRawWrite( async:%d, bytes:%d )\n",
fAsync,
cbBuffer
));
}
if ( fAsync )
{
ReferenceUlContext();
}
ulRet = HttpFilterRawWrite( sm_hFilterHandle,
_pConnectionInfo->ConnectionId,
pvBuffer,
cbBuffer,
pcbWritten,
fAsync ? QueryRawWriteOverlapped() : NULL );
if ( fAsync )
{
if ( ulRet != ERROR_IO_PENDING )
{
DereferenceUlContext();
hr = HRESULT_FROM_WIN32( ulRet );
}
}
else
{
if ( ulRet != NO_ERROR )
{
hr = HRESULT_FROM_WIN32( ulRet );
}
}
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"LEAVE DoRawWrite( async:%d, bytes:%d ) hr=0x%x\n",
fAsync,
cbBuffer,
hr
));
}
return hr;
}
HRESULT
UL_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 )
{
ReferenceUlContext();
}
ulRet = HttpFilterAppRead( sm_hFilterHandle,
_pConnectionInfo->ConnectionId,
pFilterBuffer,
pFilterBuffer->BufferSize,
pcbRead,
fAsync ? QueryAppReadOverlapped() : NULL );
if ( fAsync )
{
if ( ulRet != ERROR_IO_PENDING )
{
DereferenceUlContext();
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
UL_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 )
{
ReferenceUlContext();
}
ulRet = HttpFilterAppWrite( sm_hFilterHandle,
_pConnectionInfo->ConnectionId,
pFilterBuffer,
pFilterBuffer->BufferSize,
pcbWritten,
fAsync ? QueryAppWriteOverlapped() : NULL );
if ( fAsync )
{
if ( ulRet != ERROR_IO_PENDING )
{
DereferenceUlContext();
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
UL_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;
DWORD cbImmediate = 0;
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"ENTER DoRawRead( async:%d )\n",
fAsync
));
}
if ( fAsync )
{
ReferenceUlContext();
}
ulRet = HttpFilterRawRead( sm_hFilterHandle,
_pConnectionInfo->ConnectionId,
pvBuffer,
cbBuffer,
pcbWritten,
fAsync ? QueryRawReadOverlapped() : NULL );
if ( fAsync )
{
if ( ulRet != ERROR_IO_PENDING )
{
DereferenceUlContext();
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
UL_CONTEXT::StartClose(
VOID
)
/*++
Routine Description:
Start the process of closing the connection (and cleaning up UL_CONTEXT)
Arguments:
None
Return Value:
None
--*/
{
BOOL fOld;
fOld = (BOOL) InterlockedCompareExchange( (PLONG) &_fCloseConnection,
TRUE,
FALSE );
if ( fOld == FALSE )
{
HttpFilterClose( sm_hFilterHandle,
_pConnectionInfo->ConnectionId,
NULL );
#ifdef ISAPI
//
// Notify ISAPIs of the close
//
if ( g_pStreamFilter->QueryNotifyISAPIFilters() )
{
DBG_ASSERT( _pISAPIContext != NULL );
_pISAPIContext->ProcessConnectionClose();
}
#endif
//
// We were the ones to set the flag. Do the final dereference
//
DereferenceUlContext();
}
else
{
//
// Someone else has set the flag. Let them dereference
//
}
}
HRESULT
UL_CONTEXT::Create(
VOID
)
/*++
Routine Description:
Initialize a UL_CONTEXT
Arguments:
None
Return Value:
HRESULT
--*/
{
DBG_ASSERT( _pSSLContext == NULL );
#ifdef ISAPI
DBG_ASSERT( _pISAPIContext == NULL );
#endif
_pSSLContext = new SSL_STREAM_CONTEXT( this );
if ( _pSSLContext == NULL )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
#ifdef ISAPI
DBG_ASSERT( g_pStreamFilter != NULL );
if ( g_pStreamFilter->QueryNotifyISAPIFilters() )
{
_pISAPIContext = new ISAPI_STREAM_CONTEXT( this );
if ( _pISAPIContext == NULL )
{
delete _pSSLContext;
return HRESULT_FROM_WIN32( GetLastError() );
}
}
#endif
return NO_ERROR;
}
HRESULT
UL_CONTEXT::DoAccept(
VOID
)
/*++
Routine Description:
Accept an incoming connection by calling UlFilterAccept()
Arguments:
None
Return Value:
HRESULT
--*/
{
ULONG ulRet;
HRESULT hr = NO_ERROR;
ReferenceUlContext();
ulRet = HttpFilterAccept( sm_hFilterHandle,
_pConnectionInfo,
_buffConnectionInfo.QuerySize(),
NULL,
QueryRawReadOverlapped() );
if ( ulRet != ERROR_IO_PENDING )
{
hr = HRESULT_FROM_WIN32( ulRet );
DereferenceUlContext();
DBGPRINTF(( DBG_CONTEXT,
"Error calling UlFilterAccept(). hr = %x\n",
hr ));
}
else
{
//
// Another outstanding context available!
//
InterlockedIncrement( &sm_cOutstandingContexts );
}
return hr;
}
HRESULT
UL_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
--*/
{
HRESULT hr;
BOOL fComplete;
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
//
return _pSSLContext->SendDataBack( pRawStreamInfo );
}
//static
HRESULT
UL_CONTEXT::Initialize(
LPWSTR SslFilterChannelName
)
/*++
Routine Description:
Global Initialization
Arguments:
None
Return Value:
HRESULT
--*/
{
ULONG ulRet = ERROR_SUCCESS;
HRESULT hr = NO_ERROR;
BOOL fRet = FALSE;
//
// Get a UL handle to the RawStreamPool (or whatever)
//
ulRet = HttpCreateFilter( &sm_hFilterHandle,
SslFilterChannelName,
NULL,
HTTP_OPTION_OVERLAPPED );
if ( ulRet != ERROR_SUCCESS )
{
//
// W3SSL service may have created Filter already
// try just open it then
//
ulRet = HttpOpenFilter( &sm_hFilterHandle,
SslFilterChannelName,
HTTP_OPTION_OVERLAPPED );
if ( ulRet != ERROR_SUCCESS )
{
hr = HRESULT_FROM_WIN32( ulRet );
goto Finished;
}
}
//
// Create private thread pool for streamfilt
//
fRet = THREAD_POOL::CreateThreadPool(&sm_pThreadPool);
if (FALSE == fRet)
{
DBGPRINTF(( DBG_CONTEXT,
"Failed to create ThreadPool for Streamfilt\n" ));
hr = E_FAIL;
goto Finished;
}
DBG_ASSERT( sm_pThreadPool != NULL );
//
// Associate a completion routine with the thread pool
//
DBG_ASSERT( sm_hFilterHandle != INVALID_HANDLE_VALUE );
DBG_ASSERT( sm_hFilterHandle != NULL );
if ( !sm_pThreadPool->BindIoCompletionCallback( sm_hFilterHandle,
OverlappedCompletionRoutine,
0 ) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto Finished;
}
INITIALIZE_CRITICAL_SECTION( &sm_csUlContexts );
InitializeListHead( &sm_ListHead );
//
// Keep a set number of filter accepts outstanding
//
sm_cDesiredOutstanding = UL_CONTEXT_DESIRED_OUTSTANDING;
#if DBG
sm_pTraceLog = CreateRefTraceLog( 2000, 0 );
#endif
Finished:
if ( FAILED( hr ) )
{
if ( sm_hFilterHandle != NULL )
{
CloseHandle( sm_hFilterHandle );
sm_hFilterHandle = NULL;
}
if ( sm_pThreadPool != NULL )
{
sm_pThreadPool->TerminateThreadPool();
sm_pThreadPool = NULL;
}
}
return hr;
}
//static
VOID
UL_CONTEXT::Terminate(
VOID
)
/*++
Routine Description:
Global termination
Arguments:
None
Return Value:
None
--*/
{
if ( sm_pTraceLog != NULL )
{
DestroyRefTraceLog( sm_pTraceLog );
sm_pTraceLog = NULL;
}
if ( sm_hFilterHandle != NULL )
{
CloseHandle( sm_hFilterHandle );
sm_hFilterHandle = NULL;
}
if ( sm_pThreadPool != NULL )
{
sm_pThreadPool->TerminateThreadPool();
sm_pThreadPool = NULL;
}
DeleteCriticalSection( &sm_csUlContexts );
}
//static
VOID
UL_CONTEXT::WaitForContextDrain(
VOID
)
/*++
Routine Description:
Wait for all contexts to go away
Arguments:
None
Return Value:
None
--*/
{
while ( sm_cUlContexts != 0 )
{
DBGPRINTF(( DBG_CONTEXT,
"Waiting for %d UL_CONTEXTs to drain\n",
sm_cUlContexts ));
Sleep( 1000 );
}
//
// there should be no outstanding contexts left
//
DBG_ASSERT( sm_cOutstandingContexts == 0 );
}
//static
VOID
UL_CONTEXT::StopListening(
VOID
)
/*++
Routine Description:
Stop listening and wait for contexts to drain
Arguments:
None
Return Value:
None
--*/
{
CloseHandle( sm_hFilterHandle );
sm_hFilterHandle = NULL;
WaitForContextDrain();
}
//static
HRESULT
UL_CONTEXT::ManageOutstandingContexts(
VOID
)
{
LONG cRequired;
UL_CONTEXT * pContext;
HRESULT hr = NO_ERROR;
if ( sm_cOutstandingContexts < sm_cDesiredOutstanding )
{
cRequired = sm_cDesiredOutstanding - sm_cOutstandingContexts;
//
// Make sure the value is not negative
//
cRequired = max( 0, cRequired );
for ( LONG i = 0; i < cRequired; i++ )
{
pContext = new UL_CONTEXT();
if ( pContext == NULL )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
break;
}
hr = pContext->Create();
if ( FAILED( hr ) )
{
pContext->DereferenceUlContext();
break;
}
hr = pContext->DoAccept();
if ( FAILED( hr ) )
{
pContext->DereferenceUlContext();
break;
}
}
}
return hr;
}
VOID
OverlappedCompletionRoutine(
DWORD dwErrorCode,
DWORD dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped
)
/*++
Routine Description:
Magic completion routine
Arguments:
None
Return Value:
None
--*/
{
OVERLAPPED_CONTEXT * pContextOverlapped = NULL;
HRESULT hr;
DBG_ASSERT( lpOverlapped != NULL );
pContextOverlapped = CONTAINING_RECORD( lpOverlapped,
OVERLAPPED_CONTEXT,
_Overlapped );
DBG_ASSERT( pContextOverlapped != NULL );
pContextOverlapped->QueryContext()->ReferenceUlContext();
//
// Make up for reference which posted async operation in first place
//
pContextOverlapped->QueryContext()->DereferenceUlContext();
//
// Call the appropriate completion routine
//
switch( pContextOverlapped->QueryType() )
{
case OVERLAPPED_CONTEXT_RAW_READ:
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"ENTER OnRawReadCompletion( bytes:%d, dwErr:%d )\n",
dwNumberOfBytesTransfered,
dwErrorCode
));
}
hr = pContextOverlapped->QueryContext()->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 OVERLAPPED_CONTEXT_RAW_WRITE:
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"ENTER OnRawWriteCompletion( bytes:%d, dwErr:%d )\n",
dwNumberOfBytesTransfered,
dwErrorCode
));
}
hr = pContextOverlapped->QueryContext()->OnRawWriteCompletion(
dwNumberOfBytesTransfered,
dwErrorCode );
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"LEAVE OnRawWriteCompletion( bytes:%d, dwErr:%d ) hr=0x%x\n",
dwNumberOfBytesTransfered,
dwErrorCode,
hr
));
}
break;
case OVERLAPPED_CONTEXT_APP_READ:
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"ENTER OnAppReadCompletion( bytes:%d, dwErr:%d )\n",
dwNumberOfBytesTransfered,
dwErrorCode
));
}
hr = pContextOverlapped->QueryContext()->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 OVERLAPPED_CONTEXT_APP_WRITE:
IF_DEBUG( APP_RAW_READWRITE )
{
DBGPRINTF(( DBG_CONTEXT,
"ENTER OnAppWriteCompletion( bytes:%d, dwErr:%d )\n",
dwNumberOfBytesTransfered,
dwErrorCode
));
}
hr = pContextOverlapped->QueryContext()->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;
default:
hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
DBG_ASSERT( FALSE );
}
//
// As long as completion routine returned success, just bail. However,
// if not then we should begin the process of closing connection
//
if ( FAILED( hr ) )
{
pContextOverlapped->QueryContext()->StartClose();
}
pContextOverlapped->QueryContext()->DereferenceUlContext();
}