1042 lines
25 KiB
C++
1042 lines
25 KiB
C++
/*++
|
||
|
||
Copyright (c) 1995-1997 Microsoft Corporation
|
||
|
||
Module Name :
|
||
wamobj.cxx
|
||
|
||
Abstract:
|
||
This module implements the WAM (web application manager) object
|
||
|
||
Author:
|
||
David Kaplan ( DaveK ) 26-Feb-1997
|
||
|
||
Environment:
|
||
User Mode - Win32
|
||
|
||
Project:
|
||
Wam DLL
|
||
|
||
--*/
|
||
|
||
/************************************************************
|
||
* Include Headers
|
||
************************************************************/
|
||
#include <isapip.hxx>
|
||
#include "setable.hxx"
|
||
|
||
# include "isapi.hxx"
|
||
# include "WamW3.hxx"
|
||
|
||
#include "wamobj.hxx"
|
||
#include "iwr.h"
|
||
#include "iwr_i.c"
|
||
# include "timer.h"
|
||
|
||
#include <irtlmisc.h>
|
||
|
||
#include <ooptoken.h>
|
||
|
||
// UNDONE where do these belong?
|
||
extern PSE_TABLE g_psextensions;
|
||
|
||
|
||
class CComContextHelper
|
||
/*++
|
||
|
||
Class description:
|
||
|
||
Stack based helper class to enable calls to CoInitialize inside
|
||
ISAPI.
|
||
|
||
Replaces member functions PrepareCom/UnprepareCom in WAM_EXEC_INFO.
|
||
These had to be replaced because WAM_EXEC_INFO will persist beyond
|
||
the initial ISAPI call in the async case and keeping the call context
|
||
in member data would leak and overrelease under certain conditions.
|
||
|
||
|
||
Public Interface:
|
||
|
||
PrepareCom : For OOP get the call context and use our
|
||
private interface to enable coinit calls.
|
||
UnprepareCom : Release the call context.
|
||
|
||
--*/
|
||
{
|
||
public:
|
||
|
||
CComContextHelper( BOOL fInProcess )
|
||
: m_fInProcess( fInProcess ),
|
||
m_pComContext( NULL ),
|
||
m_pComInitsCookie( NULL )
|
||
{
|
||
}
|
||
|
||
~CComContextHelper( void )
|
||
{
|
||
UnprepareCom();
|
||
}
|
||
|
||
HRESULT PrepareCom()
|
||
/*++
|
||
Routine Description:
|
||
|
||
Prepare com before call into ISAPI. For OOP get the call context
|
||
and use our private interface to enable coinit calls.
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = NOERROR;
|
||
|
||
// Never call twice.
|
||
DBG_ASSERT( NULL == m_pComInitsCookie );
|
||
DBG_ASSERT( NULL == m_pComContext );
|
||
|
||
if( !m_fInProcess )
|
||
{
|
||
// Save COM Call Context in MTS case
|
||
hr = CoGetCallContext( IID_IComDispatchInfo, (void **)&m_pComContext );
|
||
if( SUCCEEDED(hr) )
|
||
{
|
||
hr = m_pComContext->EnableComInits( &m_pComInitsCookie );
|
||
}
|
||
}
|
||
return hr;
|
||
}
|
||
|
||
void UnprepareCom()
|
||
/*++
|
||
Routine Description:
|
||
|
||
Restores com state after call into ISAPI. Release call context.
|
||
|
||
--*/
|
||
{
|
||
// Restore COM Call Context
|
||
if( m_pComContext )
|
||
{
|
||
DBG_ASSERT( !m_fInProcess );
|
||
DBG_ASSERT( m_pComInitsCookie );
|
||
|
||
m_pComContext->DisableComInits( m_pComInitsCookie );
|
||
m_pComContext->Release();
|
||
|
||
m_pComContext = NULL;
|
||
m_pComInitsCookie = NULL;
|
||
}
|
||
}
|
||
|
||
private:
|
||
//NO-OP
|
||
CComContextHelper() {}
|
||
CComContextHelper( const CComContextHelper & ref ) {}
|
||
|
||
private:
|
||
|
||
BOOL m_fInProcess;
|
||
IComDispatchInfo * m_pComContext;
|
||
void * m_pComInitsCookie;
|
||
|
||
};
|
||
|
||
|
||
/*---------------------------------------------------------------------*
|
||
WAM::InitWam
|
||
|
||
Routine Description:
|
||
Initializes this WAM.
|
||
|
||
Arguments:
|
||
See below
|
||
|
||
Return Value:
|
||
HRESULT
|
||
|
||
*/
|
||
STDMETHODIMP
|
||
WAM::InitWam
|
||
(
|
||
BOOL fInProcess, // are we in-proc or out-of-proc?
|
||
BOOL fInPool, // !Isolated
|
||
BOOL fEnableTryExcept, // catch exceptions in ISAPI calls?
|
||
int pt, // PLATFORM_TYPE - are we running on Win95?
|
||
DWORD *pPID // Process Id of the process the wam was created in
|
||
)
|
||
{
|
||
HRESULT hr = NOERROR;
|
||
|
||
|
||
IF_DEBUG( INIT_CLEAN ) {
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"\n ********** WAM(%08x)::InitWam() *****\n",
|
||
this));
|
||
}
|
||
|
||
INITIALIZE_CRITICAL_SECTION( &m_csWamExecInfoList );
|
||
InitializeListHead( &m_WamExecInfoListHead );
|
||
|
||
m_fInProcess = fInProcess;
|
||
m_fInPool = fInPool;
|
||
|
||
DBG_ASSERT( pt != PtInvalid );
|
||
m_pt = (PLATFORM_TYPE) pt;
|
||
|
||
// Get the process id of the current process so we can return it to w3svc
|
||
*pPID = GetCurrentProcessId();
|
||
|
||
// Acquire a reference to the SE_TABLE object
|
||
DBG_REQUIRE( g_psextensions->AddRefWam() > 0);
|
||
|
||
IF_DEBUG( INIT_CLEAN) {
|
||
DBGPRINTF(( DBG_CONTEXT, "\n************Leaving WAM::InitWam ...\n" ));
|
||
}
|
||
|
||
DBG_ASSERT( SUCCEEDED(hr));
|
||
|
||
//
|
||
// UNDONE: See the DoGlobalInitializations() for details.
|
||
//
|
||
hr = DoGlobalInitializations( fInProcess, fEnableTryExcept);
|
||
|
||
return (hr);
|
||
} // WAM::InitWam()
|
||
|
||
|
||
|
||
|
||
/*---------------------------------------------------------------------*
|
||
WAM::StartShutdown
|
||
|
||
Routine Description:
|
||
Phase 1 of shutdown process.
|
||
Set the shutdown flag on the WAM object
|
||
Loop for small duration checking if all WAM_EXEC_INFO's have drained.
|
||
At the end of loop initiate the first phase of TerminateExtension()
|
||
|
||
NOTE: TerminateExtension() - currently have been tested only for the
|
||
MUST_UNLOAD option => there is no two-phase operation there.
|
||
So, we will rely on calling it once only.
|
||
|
||
Arguments:
|
||
None
|
||
|
||
Return Value:
|
||
HRESULT
|
||
|
||
*/
|
||
STDMETHODIMP
|
||
WAM::StartShutdown(
|
||
)
|
||
{
|
||
DWORD i;
|
||
|
||
IF_DEBUG( WAM ) {
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"WAM(%08x)::StartShutdown() %d Active Requests\n",
|
||
this, QueryWamStats().QueryCurrentWamRequests() ));
|
||
}
|
||
|
||
// Set the Shutting down flag to true now
|
||
DBG_REQUIRE( FALSE ==
|
||
InterlockedExchange((LPLONG)&m_fShuttingDown, (LONG)TRUE)
|
||
);
|
||
|
||
for ( i = 0;
|
||
( (i < 10) &&
|
||
(QueryWamStats().QueryCurrentWamRequests())
|
||
);
|
||
i++ )
|
||
{
|
||
# ifndef SHUTOFF_AFTER_BETA
|
||
DBGPRINTF( (
|
||
DBG_CONTEXT,
|
||
"[%d] WAM(%08x) has %d requests waiting for cleanup\n",
|
||
GetCurrentThreadId(),
|
||
this, QueryWamStats().QueryCurrentWamRequests()
|
||
) );
|
||
# endif // SHUTOFF_AFTER_BETA
|
||
|
||
Sleep( 200 ); // sleep for a while before restarting the check again
|
||
} // for
|
||
|
||
// Release the reference to the SE_TABLE object
|
||
g_psextensions->ReleaseRefWam();
|
||
|
||
return NOERROR;
|
||
} // WAM::StartShutdown()
|
||
|
||
|
||
|
||
|
||
/*---------------------------------------------------------------------*
|
||
WAM::UninitWam
|
||
|
||
Routine Description:
|
||
Phase 2 of shutdown process.
|
||
|
||
Arguments:
|
||
None
|
||
|
||
Return Value:
|
||
HRESULT
|
||
NOERROR on success
|
||
E_FAIL if there are any pending items to be deleted still
|
||
|
||
*/
|
||
STDMETHODIMP
|
||
WAM::UninitWam()
|
||
{
|
||
//
|
||
// If there are any pending requests being processed, wait for them
|
||
// to be drained off from this WAM
|
||
//
|
||
|
||
IF_DEBUG( WAM ) {
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"WAM(%08x)::UninitWam() %d Active Requests\n",
|
||
this, QueryWamStats().QueryCurrentWamRequests() ));
|
||
}
|
||
|
||
if ( QueryWamStats().QueryCurrentWamRequests() != 0 )
|
||
{
|
||
IF_DEBUG( ERROR) {
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"WAM(%08x)::UninitWam() Error - "
|
||
" Failed with active requests! (%d active)\n",
|
||
this,
|
||
QueryWamStats().QueryCurrentWamRequests() ));
|
||
}
|
||
|
||
//
|
||
// Enumerate and dump information on all requests that are hanging
|
||
// and the associated ISAPI DLLs
|
||
//
|
||
# ifndef SHUTOFF_AFTER_BETA
|
||
while ( QueryWamStats().QueryCurrentWamRequests() > 0) {
|
||
|
||
DBGPRINTF(
|
||
( DBG_CONTEXT,
|
||
"\n\n[Thd %d]WAM(%08x) has %d requests waiting ... \n",
|
||
GetCurrentThreadId(),
|
||
this, QueryWamStats().QueryCurrentWamRequests()
|
||
) );
|
||
|
||
g_psextensions->PrintRequestCounts();
|
||
|
||
// sleep for a while before restarting the check again
|
||
Sleep( 1000 );
|
||
} // while
|
||
|
||
# endif // SHUTOFF_AFTER_BETA
|
||
|
||
|
||
// return failure, since we failed to shutdown gracefully!
|
||
// NYI: Should I ignore the fact that some long-hanging connections
|
||
// are okay?
|
||
// Shouldn't I be forcing the exit now?
|
||
DBG_ASSERT( QueryWamStats().QueryCurrentWamRequests() == 0);
|
||
// return ( E_FAIL);
|
||
}
|
||
|
||
DeleteCriticalSection( &m_csWamExecInfoList );
|
||
|
||
return NOERROR;
|
||
} // WAM::UninitWam()
|
||
|
||
|
||
/*-----------------------------------------------------------------------------*
|
||
WAM::ProcessAsyncIO
|
||
Completes an async i/o process for a given wam request.
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
HRESULT
|
||
|
||
*/
|
||
STDMETHODIMP
|
||
WAM::ProcessAsyncIO
|
||
(
|
||
#ifdef _WIN64
|
||
UINT64 pWamExecInfoIn, // WAM_EXEC_INFO *
|
||
#else
|
||
DWORD_PTR pWamExecInfoIn, // WAM_EXEC_INFO *
|
||
#endif
|
||
DWORD dwStatus,
|
||
DWORD cbWritten
|
||
)
|
||
{
|
||
return ProcessAsyncIOImpl( pWamExecInfoIn,
|
||
dwStatus,
|
||
cbWritten
|
||
);
|
||
|
||
} // WAM::ProcessAsyncIO
|
||
|
||
STDMETHODIMP
|
||
WAM::ProcessAsyncReadOop
|
||
(
|
||
#ifdef _WIN64
|
||
UINT64 pWamExecInfoIn,
|
||
#else
|
||
DWORD_PTR pWamExecInfoIn,
|
||
#endif
|
||
DWORD dwStatus,
|
||
DWORD cbRead,
|
||
unsigned char * lpDataRead
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
Handle callback for out of process AsyncRead
|
||
|
||
Arguments:
|
||
|
||
pWamExecInfoIn - The smuggled pointer to the WAM_EXEC_INFO
|
||
dwStatus - IO status
|
||
cbRead - Number of bytes read
|
||
lpDataRead - Marshalled data read
|
||
|
||
Return Value:
|
||
|
||
Notes:
|
||
|
||
The initial design for AsyncRead was inadequate when the isapi is
|
||
running out of process. The problem was that the data buffer was
|
||
marshalled over to inetinfo and back during the AsyncRead call.
|
||
Since the completion will happen on another thread the ISAPI could
|
||
get the completion before the data was completely marshalled back
|
||
or the address in inetinfo could be invalidated before the read was
|
||
complete. The solution is to add a separate path for oop async reads
|
||
and marshall the data on the io completion and copy it into the
|
||
client's buffer then.
|
||
|
||
--*/
|
||
{
|
||
DBG_ASSERT( lpDataRead != NULL );
|
||
DBG_ASSERT( !m_fInProcess );
|
||
|
||
return ProcessAsyncIOImpl( pWamExecInfoIn,
|
||
dwStatus,
|
||
cbRead,
|
||
lpDataRead
|
||
);
|
||
}
|
||
|
||
HRESULT
|
||
WAM::ProcessAsyncIOImpl
|
||
(
|
||
#ifdef _WIN64
|
||
UINT64 pWamExecInfoIn,
|
||
#else
|
||
DWORD_PTR pWamExecInfoIn,
|
||
#endif
|
||
DWORD dwStatus,
|
||
DWORD cb,
|
||
LPBYTE lpDataRead // = NULL
|
||
)
|
||
{
|
||
HRESULT hrRet;
|
||
CComContextHelper callContext( m_fInProcess );
|
||
|
||
WAM_EXEC_INFO * pWamExecInfo =
|
||
reinterpret_cast<WAM_EXEC_INFO *>(pWamExecInfoIn);
|
||
|
||
//
|
||
// NOTE we assert because we believe the pointer can never be null
|
||
// AND we fail gracefully if it is null because there is a long
|
||
// code path across many threads between setting the pointer
|
||
// and here, and you never know ...
|
||
//
|
||
|
||
DBG_ASSERT ( pWamExecInfo != NULL );
|
||
|
||
if ( pWamExecInfo == NULL ) {
|
||
|
||
return HRESULT_FROM_WIN32( E_POINTER );
|
||
}
|
||
|
||
//
|
||
// Note: The AddRef/Release calls may not be necessary anymore.
|
||
//
|
||
// Make sure that UnprepareCom has valid WamInfo
|
||
//
|
||
|
||
pWamExecInfo->AddRef();
|
||
|
||
callContext.PrepareCom();
|
||
|
||
if( lpDataRead == NULL )
|
||
{
|
||
// Doing an out of process async read
|
||
|
||
hrRet = HresultFromBool(
|
||
pWamExecInfo->ProcessAsyncIO(
|
||
dwStatus,
|
||
cb
|
||
) );
|
||
}
|
||
else
|
||
{
|
||
// All other async io completions
|
||
|
||
hrRet = HresultFromBool(
|
||
pWamExecInfo->ProcessAsyncReadOop(
|
||
dwStatus,
|
||
cb,
|
||
lpDataRead
|
||
) );
|
||
}
|
||
|
||
callContext.UnprepareCom();
|
||
|
||
//
|
||
// Balance AddRef() above
|
||
//
|
||
|
||
pWamExecInfo->Release();
|
||
|
||
return hrRet;
|
||
}
|
||
|
||
|
||
/*-----------------------------------------------------------------------------*
|
||
WAM::ProcessRequest
|
||
Processes a WAM request.
|
||
|
||
Arguments:
|
||
pIWamRequest - pointer to IWamRequest interface
|
||
cbWrcStrings - Count of bytes for wamreq core strings
|
||
pfHandled - Indicates we handled this request
|
||
pfFinished - Indicates no further processing is required
|
||
|
||
Return Value:
|
||
HRESULT
|
||
|
||
*/
|
||
STDMETHODIMP
|
||
WAM::ProcessRequest
|
||
(
|
||
IWamRequest * pIWamRequest,
|
||
DWORD cbWrcStrings,
|
||
OOP_CORE_STATE * pOopCoreState,
|
||
BOOL * pfHandled
|
||
)
|
||
{
|
||
HRESULT hrRet = NOERROR; // this function's return value
|
||
HRESULT hr;
|
||
int iretInvokeExt; // return value from InvokeExtension
|
||
BOOL fFreeContext = TRUE;// do we need to free up wamex-info?
|
||
WAM_EXEC_INFO * pWamExecInfo = NULL;
|
||
BOOL fImpersonated = FALSE;
|
||
BYTE * pbEntityBody = NULL;
|
||
DWORD cbEntityBody = 0;
|
||
CComContextHelper callContext( m_fInProcess );
|
||
|
||
|
||
DBG_ASSERT( pIWamRequest );
|
||
IF_DEBUG( WAM) {
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"WAM(%08x)::ProcessRequest(%08x, %08x, ... )\n",
|
||
this, pIWamRequest ));
|
||
}
|
||
|
||
|
||
if ( m_fShuttingDown ) {
|
||
|
||
IF_DEBUG( WAM) {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT
|
||
, "WAM(%08x) shutting down. "
|
||
"Request(%08x) will be aborted.\n"
|
||
, this
|
||
, pIWamRequest
|
||
));
|
||
}
|
||
|
||
// UNDONE something besides E_FAIL?
|
||
return E_FAIL;
|
||
|
||
}
|
||
|
||
|
||
// create, init the wamexec-info and add it to list
|
||
pWamExecInfo = new WAM_EXEC_INFO( this );
|
||
if( NULL == pWamExecInfo )
|
||
{
|
||
hrRet = E_OUTOFMEMORY;
|
||
goto LError;
|
||
}
|
||
|
||
//
|
||
// Update the statistics counters
|
||
//
|
||
|
||
m_WamStats.IncrWamRequests();
|
||
|
||
|
||
if ( FAILED( hrRet = pWamExecInfo->InitWamExecInfo( pIWamRequest,
|
||
cbWrcStrings,
|
||
pOopCoreState
|
||
) ) )
|
||
{
|
||
goto LError;
|
||
}
|
||
|
||
if( !m_fInProcess )
|
||
{
|
||
//
|
||
// This test shouldn't really be necessary. In the case where com plus
|
||
// activates us in process even if we are marked to run in the
|
||
// surrogate, we will fail the app creation.
|
||
//
|
||
DBG_ASSERT( CWamOopTokenInfo::HasInstance() );
|
||
if( CWamOopTokenInfo::HasInstance() )
|
||
{
|
||
hrRet = CWamOopTokenInfo::QueryInstance()->ModifyTokenForOop
|
||
(
|
||
WRC_GET_FIX.m_hUserToken
|
||
);
|
||
|
||
if( FAILED(hrRet) )
|
||
{
|
||
goto LError;
|
||
}
|
||
}
|
||
}
|
||
|
||
this->InsertIntoList( &pWamExecInfo->_ListEntry);
|
||
|
||
|
||
DBG_WAMREQ_REFCOUNTS(( "WAM::ProcessRequest right after wrc construction ...", pWamExecInfo ));
|
||
|
||
if ( !FWin95() )
|
||
{
|
||
if ( !ImpersonateLoggedOnUser( WRC_GET_FIX.m_hUserToken ) )
|
||
{
|
||
IF_DEBUG( ERROR ) {
|
||
DBGPRINTF((DBG_CONTEXT,
|
||
"WAM(%08x) ImpersonateLoggedOnUser(%08x)"
|
||
"failed[err %d]\n",
|
||
this, WRC_GET_FIX.m_hUserToken, GetLastError()));
|
||
}
|
||
|
||
hrRet = HresultFromGetLastError();
|
||
goto LError;
|
||
}
|
||
fImpersonated = TRUE;
|
||
}
|
||
|
||
pWamExecInfo->_psExtension = NULL;
|
||
|
||
if ( ! g_psextensions->GetExtension( WRC_GET_SZ( WRC_I_ISADLLPATH ),
|
||
WRC_GET_FIX.m_hUserToken,
|
||
WRC_GET_FIX.m_fAnonymous,
|
||
WRC_GET_FIX.m_fCacheISAPIApps,
|
||
&(pWamExecInfo->_psExtension ) ) )
|
||
{
|
||
|
||
hrRet = HresultFromGetLastError( );
|
||
goto LError;
|
||
|
||
}
|
||
|
||
// Add a reference to ensure that wamexec is valid until we hit the
|
||
// cleanup code. Don't put any goto LError after this or the wamexec
|
||
// will leak.
|
||
pWamExecInfo->AddRef();
|
||
|
||
// Invoke the server extension
|
||
callContext.PrepareCom();
|
||
iretInvokeExt = InvokeExtension( pWamExecInfo->_psExtension,
|
||
WRC_GET_SZ( WRC_I_ISADLLPATH ),
|
||
pWamExecInfo );
|
||
callContext.UnprepareCom();
|
||
|
||
if ( fImpersonated ) {
|
||
::RevertToSelf( );
|
||
fImpersonated = FALSE;
|
||
}
|
||
|
||
pWamExecInfo->_dwFlags &= ~SE_PRIV_FLAG_IN_CALLBACK;
|
||
|
||
switch ( iretInvokeExt )
|
||
{
|
||
|
||
case HSE_STATUS_PENDING: {
|
||
|
||
IF_DEBUG( WAM_EXEC ) {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT
|
||
, "WAM(%08x)::ProcessRequest case HSE_STATUS_PENDING\n"
|
||
, this
|
||
));
|
||
}
|
||
|
||
IF_DEBUG( WAM_REFCOUNTS ) {
|
||
|
||
DBG_WAMREQ_REFCOUNTS((
|
||
"WAM::ProcessRequest case HSE_STATUS_PENDING ",
|
||
pWamExecInfo
|
||
));
|
||
}
|
||
|
||
//
|
||
// Figure out whether this mainline thread or callback thread
|
||
// hit its cleanup code first
|
||
//
|
||
// This protects us against isapis that disobey the async rules.
|
||
// The isapi should be in one of two modes:
|
||
//
|
||
// 1. It return HSE_STATUS_PENDING in the mainline thread and
|
||
// always calls HSE_DONE_WITH_SESSION.
|
||
//
|
||
// 2. It returns any other status code from the mainline and
|
||
// NEVER calls HSE_DONE_WITH_SESSION.
|
||
//
|
||
// Unfortunately isapi writers frequently do bad things to good
|
||
// servers.
|
||
//
|
||
// NOTE return value of INTERLOCKED_COMPARE_EXCHANGE
|
||
// is initial value of the destination
|
||
//
|
||
|
||
LONG FirstThread = INTERLOCKED_COMPARE_EXCHANGE(
|
||
(LONG *) &pWamExecInfo->_FirstThread
|
||
, (LONG) FT_MAINLINE
|
||
, (LONG) FT_NULL
|
||
);
|
||
|
||
if( FirstThread == (LONG) FT_CALLBACK ) {
|
||
|
||
//
|
||
// If we made it here, then we need to do cleanup, meaning:
|
||
// - SSF HSE_REQ_DONE_WITH_SESSION callback has already run
|
||
// - we set fFreeContext TRUE to trigger cleanup below
|
||
//
|
||
|
||
fFreeContext = TRUE;
|
||
|
||
IF_DEBUG( WAM_EXEC ) {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT
|
||
, "\tSession done.\n"
|
||
));
|
||
}
|
||
|
||
} else {
|
||
|
||
DBG_ASSERT( FirstThread == (LONG) FT_NULL );
|
||
|
||
//
|
||
// If we made it here, then we can't cleanup yet, meaning:
|
||
// - SSF HSE_REQ_DONE_WITH_SESSION callback will cleanup
|
||
// when it runs
|
||
// - we set fFreeContext FALSE to avoid cleanup below
|
||
//
|
||
fFreeContext = FALSE;
|
||
|
||
IF_DEBUG( WAM_EXEC ) {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT
|
||
, "\tSession NOT done.\n"
|
||
));
|
||
}
|
||
}
|
||
|
||
//
|
||
// If fFreeContext is FALSE at this point, we may not use
|
||
// pWamExecInfo from this point on - callback thread can
|
||
// invalidate it at any time.
|
||
//
|
||
|
||
break;
|
||
} // case HSE_STATUS_PENDING
|
||
|
||
|
||
case HSE_STATUS_ERROR:
|
||
case HSE_STATUS_SUCCESS:
|
||
|
||
//
|
||
// in error and success cases we no-op
|
||
//
|
||
|
||
break;
|
||
|
||
case HSE_STATUS_SUCCESS_AND_KEEP_CONN:
|
||
|
||
//
|
||
// remember that ISA asked us to set keep-conn
|
||
//
|
||
|
||
pWamExecInfo->_dwIsaKeepConn = KEEPCONN_TRUE;
|
||
break;
|
||
|
||
default:
|
||
|
||
break;
|
||
} // switch()
|
||
|
||
// Release the ref held by this call
|
||
pWamExecInfo->Release();
|
||
|
||
if ( fFreeContext ) {
|
||
|
||
DBG_ASSERT( pWamExecInfo != NULL);
|
||
|
||
DBG_WAMREQ_REFCOUNTS(( "WAM::ProcessRequest fFreeContext ...",
|
||
pWamExecInfo));
|
||
|
||
|
||
|
||
pWamExecInfo->CleanupAndRelease( TRUE );
|
||
|
||
pWamExecInfo = NULL;
|
||
|
||
} // if ( fFreeContext);
|
||
|
||
*pfHandled = TRUE;
|
||
DBG_ASSERT( hrRet == NOERROR );
|
||
|
||
|
||
LExit:
|
||
|
||
if ( fImpersonated ) {
|
||
|
||
::RevertToSelf( );
|
||
fImpersonated = FALSE;
|
||
}
|
||
|
||
return hrRet;
|
||
|
||
|
||
LError:
|
||
|
||
//
|
||
// release pWamExecInfo on failure case
|
||
// NOTE we separate this from normal exit code because wamexecinfo
|
||
// must hang around in many non-error cases (status-pending, async i/o)
|
||
//
|
||
|
||
if ( pWamExecInfo != NULL) {
|
||
|
||
pWamExecInfo->CleanupAndRelease( FALSE );
|
||
|
||
pWamExecInfo = NULL;
|
||
}
|
||
|
||
|
||
goto LExit;
|
||
|
||
} // WAM::ProcessRequest()
|
||
|
||
|
||
|
||
STDMETHODIMP
|
||
WAM::GetStatistics(
|
||
/*[in]*/ DWORD dwLevel,
|
||
/*[out, switch_is(Level)]*/
|
||
LPWAM_STATISTICS_INFO pWamStatsInfo
|
||
)
|
||
/*++
|
||
Description:
|
||
Obtains the statistics for the given instance of WAM for specified level.
|
||
|
||
Arguments:
|
||
dwLevel - specifies the information level. (Currently level 0 is supported)
|
||
pWamStatsInfo - pointer to the WAM STATISTICS INFO object that will
|
||
receive the statistics.
|
||
|
||
Returns:
|
||
HRESULT - NOERROR on success and E_FAIL on failure.
|
||
--*/
|
||
{
|
||
HRESULT hr = NOERROR;
|
||
|
||
DBG_ASSERT( pWamStatsInfo != NULL);
|
||
|
||
IF_DEBUG( API_ENTRY) {
|
||
DBGPRINTF(( DBG_CONTEXT, "WAM(%08x)::GetStatistics(%d, %08x)\n",
|
||
this, dwLevel, pWamStatsInfo));
|
||
}
|
||
|
||
switch ( dwLevel) {
|
||
case 0: {
|
||
// copy values out of the statistics structure
|
||
m_WamStats.CopyToStatsBuffer( &pWamStatsInfo->WamStats0);
|
||
break;
|
||
}
|
||
|
||
default:
|
||
DBG_ASSERT( FALSE);
|
||
hr = E_FAIL;
|
||
break;
|
||
|
||
} // switch()
|
||
|
||
return (hr);
|
||
|
||
} // WAM::GetStatistics()
|
||
|
||
|
||
|
||
/*++
|
||
WAM::InvokeExtension
|
||
|
||
Routine Description:
|
||
|
||
Invokes a server extension.
|
||
NOTE without this cover function, we get a compile error
|
||
error C2712: Cannot use __try in functions that require object unwinding
|
||
|
||
Arguments:
|
||
|
||
psExt - pointer to server extension
|
||
szISADllPath - Fully qualified path to Module (DLL name)
|
||
pWamExecInfo - ptr to wamexec info
|
||
|
||
Return Value:
|
||
|
||
DWORD - HSE_STATUS_ code
|
||
|
||
--*/
|
||
DWORD
|
||
WAM::InvokeExtension
|
||
(
|
||
IN PHSE psExt,
|
||
const char * szISADllPath,
|
||
WAM_EXEC_INFO * pWamExecInfo
|
||
)
|
||
{
|
||
|
||
DWORD ret;
|
||
|
||
//
|
||
// Protect the call to the server extension so we don't hose the
|
||
// server
|
||
//
|
||
|
||
__try
|
||
{
|
||
ret = psExt->ExecuteRequest( pWamExecInfo );
|
||
}
|
||
__except ( g_fEnableTryExcept ?
|
||
WAMExceptionFilter( GetExceptionInformation(),
|
||
WAM_EVENT_EXTENSION_EXCEPTION,
|
||
pWamExecInfo ) :
|
||
EXCEPTION_CONTINUE_SEARCH )
|
||
{
|
||
|
||
//
|
||
// exception caused us to to leave HSE_APPDLL::ExecuteRequest
|
||
// with unbalanced AddRef()
|
||
//
|
||
|
||
pWamExecInfo->Release();
|
||
|
||
ret = HSE_STATUS_ERROR;
|
||
}
|
||
|
||
return ret;
|
||
} // WAM::InvokeExtension
|
||
|
||
|
||
/*++
|
||
WAM::HseReleaseExtension
|
||
|
||
Routine Description:
|
||
|
||
Releases a server extension.
|
||
|
||
Arguments:
|
||
|
||
psExt - pointer to server extension.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
VOID
|
||
WAM::HseReleaseExtension
|
||
(
|
||
IN PHSE psExt
|
||
)
|
||
{
|
||
g_psextensions->ReleaseExtension( psExt);
|
||
} // HseReleaseExtension()
|
||
|
||
|
||
|
||
|
||
|
||
/************************************************************
|
||
* Member Functions of WAM_STATISTICS
|
||
************************************************************/
|
||
|
||
|
||
WAM_STATISTICS::WAM_STATISTICS( VOID)
|
||
/*++
|
||
Initializes statistics information for server.
|
||
--*/
|
||
{
|
||
INITIALIZE_CRITICAL_SECTION( & m_csStatsLock);
|
||
ClearStatistics();
|
||
|
||
} // WAM_STATISTICS::WAM_STATISTICS();
|
||
|
||
|
||
VOID
|
||
WAM_STATISTICS::ClearStatistics( VOID)
|
||
/*++
|
||
|
||
Clears the counters used for statistics information
|
||
|
||
--*/
|
||
{
|
||
LockStatistics();
|
||
|
||
memset( &m_WamStats, 0, sizeof(WAM_STATISTICS_0) );
|
||
m_WamStats.TimeOfLastClear = GetCurrentTimeInSeconds();
|
||
|
||
UnlockStatistics();
|
||
|
||
} // WAM_STATISTICS::ClearStatistics()
|
||
|
||
|
||
|
||
DWORD
|
||
WAM_STATISTICS::CopyToStatsBuffer( PWAM_STATISTICS_0 pStat0)
|
||
/*++
|
||
Description:
|
||
copies the statistics data from the server statistcs structure
|
||
to the WAM_STATISTICS_0 structure for RPC access.
|
||
|
||
Arugments:
|
||
pStat0 pointer to WAM_STATISTICS_0 object which contains the
|
||
data on successful return
|
||
|
||
Returns:
|
||
Win32 error codes. NO_ERROR on success.
|
||
|
||
--*/
|
||
{
|
||
|
||
DBG_ASSERT( pStat0 != NULL);
|
||
|
||
LockStatistics();
|
||
|
||
CopyMemory( pStat0, &m_WamStats, sizeof(WAM_STATISTICS_0) );
|
||
|
||
UnlockStatistics();
|
||
|
||
return ( NO_ERROR);
|
||
|
||
} // WAM_STATISTICS::CopyToStatsBuffer()
|
||
|
||
/************************ End of File ***********************/
|