/*++

   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();
}