1815 lines
40 KiB
C++
1815 lines
40 KiB
C++
/*---------------------------------------------------------------------*
|
||
|
||
Copyright (c) 1995-1996 Microsoft Corporation
|
||
|
||
Module Name :
|
||
|
||
wamxinfo.cxx (formerly seinfo.cxx)
|
||
|
||
Abstract:
|
||
|
||
Implementation of WAM_EXEC_INFO object.
|
||
|
||
Authors:
|
||
|
||
Murali R. Krishnan ( MuraliK ) 18-July-1996
|
||
David Kaplan ( DaveK ) 10-July-1997
|
||
|
||
Environment:
|
||
|
||
User Mode - Win32
|
||
|
||
Project:
|
||
|
||
Wam DLL
|
||
--*/
|
||
|
||
|
||
/************************************************************
|
||
* Include Headers
|
||
************************************************************/
|
||
# include <isapip.hxx>
|
||
# include "wamxinfo.hxx"
|
||
# include "WReqCore.hxx"
|
||
# include "setable.hxx"
|
||
# include "gip.h"
|
||
# include "WamW3.hxx"
|
||
|
||
// MIDL-generated
|
||
# include "iwr.h"
|
||
|
||
// allocation cache for the WAM_EXEC_INFO objects
|
||
ALLOC_CACHE_HANDLER * WAM_EXEC_INFO::sm_pachExecInfo;
|
||
# define WAM_EXEC_INFO_CACHE_THRESHOLD (400) // UNDONE: Empirically vary
|
||
|
||
#if DBG
|
||
PTRACE_LOG WAM_EXEC_INFO::sm_pDbgRefTraceLog;
|
||
#endif
|
||
|
||
SV_CACHE_MAP * WAM_EXEC_INFO::sm_pSVCacheMap = NULL;
|
||
|
||
//
|
||
// Ref count trace log sizes for per-request log and global log
|
||
// (used for debugging ref count problems)
|
||
//
|
||
// NOTE these are large because WAM_EXEC_INFO can have a lot of
|
||
// ref/deref activity
|
||
//
|
||
|
||
#define C_REFTRACES_PER_REQUEST 128
|
||
#define C_REFTRACES_GLOBAL 4096
|
||
|
||
|
||
/************************************************************
|
||
* Functions
|
||
************************************************************/
|
||
|
||
|
||
|
||
/*---------------------------------------------------------------------*
|
||
WAM_EXEC_INFO::WAM_EXEC_INFO
|
||
Constructor
|
||
|
||
Arguments:
|
||
pWam - ptr to wam
|
||
|
||
Returns:
|
||
Nothing
|
||
|
||
*/
|
||
WAM_EXEC_INFO::WAM_EXEC_INFO(
|
||
PWAM pWam
|
||
)
|
||
{
|
||
_cRefs = 1;
|
||
m_pWam= pWam;
|
||
_psExtension = NULL;
|
||
_FirstThread = FT_NULL;
|
||
m_dwSignature = WAM_EXEC_INFO_SIGNATURE;
|
||
|
||
DBG_ASSERT( m_pWam );
|
||
|
||
m_fInProcess = m_pWam->FInProcess();
|
||
m_fInPool = m_pWam->FInPool();
|
||
|
||
m_fDisconnected = FALSE;
|
||
|
||
InitializeListHead( &_ListEntry);
|
||
|
||
IF_DEBUG( WAM_EXEC ) {
|
||
|
||
DBGPRINTF(( DBG_CONTEXT, "WAM_EXEC_INFO(%p) Ctor : %d -> %d\n",
|
||
this, _cRefs-1, _cRefs ));
|
||
|
||
}
|
||
|
||
#if DBG
|
||
// create ref trace log
|
||
m_pDbgRefTraceLog = CreateRefTraceLog( C_REFTRACES_PER_REQUEST, 0 );
|
||
#endif
|
||
|
||
} // WAM_EXEC_INFO::WAM_EXEC_INFO
|
||
|
||
|
||
/*---------------------------------------------------------------------*
|
||
WAM_EXEC_INFO::~WAM_EXEC_INFO
|
||
Destructor
|
||
|
||
Arguments:
|
||
None
|
||
|
||
Returns:
|
||
Nothing
|
||
|
||
*/
|
||
WAM_EXEC_INFO::~WAM_EXEC_INFO(
|
||
)
|
||
{
|
||
|
||
IF_DEBUG( WAM_EXEC ) {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT
|
||
, "WAM_EXEC_INFO(%p) Dtor \n"
|
||
, this
|
||
));
|
||
}
|
||
|
||
m_dwSignature = WAM_EXEC_INFO_SIGNATURE_FREE;
|
||
|
||
#if DBG
|
||
// write thread id into second dword of this object's memory
|
||
// (alloc cache stores next-ptr in 1st dword, so we use 2nd slot)
|
||
*( (DWORD *)this + 1 ) = GetCurrentThreadId();
|
||
|
||
// destroy ref trace log
|
||
if( m_pDbgRefTraceLog != NULL ) {
|
||
DestroyRefTraceLog( m_pDbgRefTraceLog );
|
||
}
|
||
|
||
#endif
|
||
|
||
} // WAM_EXEC_INFO::~WAM_EXEC_INFO
|
||
|
||
|
||
|
||
#define WRC_F _WamReqCore.m_WamReqCoreFixed
|
||
/*---------------------------------------------------------------------*
|
||
WAM_EXEC_INFO::InitWamExecInfo
|
||
Initializes the wamexec info
|
||
|
||
Arguments:
|
||
cbWrcStrings - number of bytes required by strings buffer
|
||
dwChildFlags - flags for child execution (HSE_EXEC_???)
|
||
|
||
Returns:
|
||
HRESULT
|
||
|
||
*/
|
||
HRESULT
|
||
WAM_EXEC_INFO::InitWamExecInfo
|
||
(
|
||
IWamRequest * pIWamRequest,
|
||
DWORD cbWrcStrings,
|
||
OOP_CORE_STATE * pOopCoreState
|
||
)
|
||
{
|
||
HRESULT hr = NOERROR;
|
||
|
||
DBG_ASSERT( pIWamRequest );
|
||
|
||
IF_DEBUG( WAM_EXEC ) {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT
|
||
, "WAM_EXEC_INFO(%p)::InitWamExecInfo "
|
||
"pIWamRequest(%p) "
|
||
"cbWrcStrings(%d) "
|
||
"m_fInProcess(%d) "
|
||
"\n"
|
||
, this
|
||
, pIWamRequest
|
||
, cbWrcStrings
|
||
, m_fInProcess
|
||
));
|
||
|
||
}
|
||
|
||
|
||
if ( FAILED( hr = InitWamExecBase( pIWamRequest ) ) ) {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT
|
||
, "InitWamExecBase failed "
|
||
"hr(%x) "
|
||
"\n"
|
||
, hr
|
||
));
|
||
|
||
goto LExit;
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Init wamreq core - contains stuff needed by ecb
|
||
//
|
||
|
||
if ( FAILED( hr = _WamReqCore.InitWamReqCore(
|
||
cbWrcStrings
|
||
, pIWamRequest
|
||
, pOopCoreState
|
||
, m_fInProcess
|
||
) ) )
|
||
{
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT
|
||
, "InitWamReqCore failed "
|
||
"hr(%x) "
|
||
"\n"
|
||
, hr
|
||
));
|
||
|
||
goto LExit;
|
||
}
|
||
|
||
//
|
||
// If this is a child ISA, set appropriate flags
|
||
//
|
||
|
||
_dwChildExecFlags = _WamReqCore.m_WamReqCoreFixed.m_dwChildExecFlags;
|
||
|
||
|
||
IF_DEBUG( WAM_EXEC ) {
|
||
|
||
Print();
|
||
}
|
||
|
||
//
|
||
// begin Reset_Code
|
||
// [the following code was formerly WAM_EXEC_INFO::Reset]
|
||
//
|
||
|
||
_dwFlags = SE_PRIV_FLAG_IN_CALLBACK;
|
||
_AsyncIoInfo._pfnHseIO = NULL;
|
||
_AsyncIoInfo._pvHseIOContext = NULL;
|
||
_AsyncIoInfo._dwOutstandingIO = ASYNC_IO_TYPE_NONE;
|
||
_AsyncIoInfo._cbLastAsyncIO = 0;
|
||
_AsyncIoInfo._pvAsyncReadBuffer = NULL;
|
||
|
||
// we are either inproc-valid or oop-valid, not both
|
||
DBG_ASSERT(
|
||
( m_fInProcess && AssertInpValid() && !AssertOopValid() )
|
||
||
|
||
(!m_fInProcess && !AssertInpValid() && AssertOopValid() )
|
||
);
|
||
|
||
ecb.cbSize = sizeof(EXTENSION_CONTROL_BLOCK);
|
||
|
||
//
|
||
// dwVersion is hardcoded because the iisext.h file that defines
|
||
// HSE_VERSION_MAJOR and HSE_VERSION_MINOR is shipped as a part of
|
||
// the SDK, and the compiler variable that distinguishes 5.1 from
|
||
// 6.0 builds is internal. The #if directive wouldn't make sense
|
||
// in a file exposed to the public.
|
||
//
|
||
|
||
ecb.dwVersion = MAKELONG( 1, 5 );
|
||
|
||
// keep in sync with HT_OK in basereq.hxx
|
||
#define HT_OK 200
|
||
|
||
ecb.dwHttpStatusCode = HT_OK;
|
||
ecb.lpszLogData[0] = '\0';
|
||
|
||
//
|
||
// note that function pointers are set in isplocal.cxx before
|
||
// executing the request using ECB
|
||
//
|
||
|
||
ecb.ConnID = (HCONN) this;
|
||
ecb.lpszMethod = _WamReqCore.GetSz( WRC_I_METHOD );
|
||
ecb.lpszQueryString = _WamReqCore.GetSz( WRC_I_QUERY );
|
||
ecb.lpszPathInfo = _WamReqCore.GetSz( WRC_I_PATHINFO );
|
||
ecb.lpszContentType = _WamReqCore.GetSz( WRC_I_CONTENTTYPE );
|
||
ecb.lpszPathTranslated = _WamReqCore.GetSz( WRC_I_PATHTRANS );
|
||
ecb.cbTotalBytes = WRC_F.m_cbClientContent;
|
||
|
||
//
|
||
// Clients can send more bytes then are indicated in their
|
||
// Content-Length header. Adjust byte counts so they match
|
||
//
|
||
|
||
ecb.cbAvailable = (WRC_F.m_cbEntityBody > WRC_F.m_cbClientContent)
|
||
? WRC_F.m_cbClientContent
|
||
: WRC_F.m_cbEntityBody
|
||
;
|
||
|
||
ecb.lpbData = _WamReqCore.m_pbEntityBody;
|
||
|
||
//
|
||
// end Reset_Code
|
||
// [the preceding code was formerly WAM_EXEC_INFO::Reset]
|
||
//
|
||
|
||
|
||
LExit:
|
||
return hr;
|
||
|
||
} // WAM_EXEC_INFO::InitWamExecInfo
|
||
|
||
HRESULT
|
||
WAM_EXEC_INFO::GetInfoForName
|
||
(
|
||
IWamRequest * pIWamRequest,
|
||
const unsigned char * szVarName,
|
||
unsigned char * pchBuffer,
|
||
DWORD cchBuffer,
|
||
DWORD * pcchRequired
|
||
)
|
||
{
|
||
HRESULT hr = NOERROR;
|
||
BOOL fCacheHit = FALSE;
|
||
|
||
if( !m_fInProcess )
|
||
{
|
||
// Lookup server variable in the cache.
|
||
|
||
DBG_ASSERT( sm_pSVCacheMap );
|
||
|
||
LPCSTR szTempVarName = (LPSTR)szVarName;
|
||
DWORD dwOrdinal;
|
||
|
||
if( sm_pSVCacheMap->FindOrdinal( szTempVarName,
|
||
strlen(szTempVarName),
|
||
&dwOrdinal
|
||
) )
|
||
{
|
||
DBG_ASSERT( dwOrdinal < SVID_COUNT );
|
||
|
||
DWORD dwOffset = _WamReqCore.m_rgSVOffsets[dwOrdinal];
|
||
|
||
if( dwOffset != SV_DATA_INVALID_OFFSET )
|
||
{
|
||
DBG_ASSERT( _WamReqCore.m_pbSVData );
|
||
|
||
// We have a value cached.
|
||
fCacheHit = TRUE;
|
||
|
||
if( SUCCEEDED(dwOffset) )
|
||
{
|
||
// The offset is an actual offset into our data buffer iff
|
||
// the high bit isn't set.
|
||
LPSTR szValue = (LPSTR)(_WamReqCore.m_pbSVData + dwOffset);
|
||
DWORD cchValue = strlen( szValue ) + 1;
|
||
|
||
if( cchValue > cchBuffer || pchBuffer == NULL )
|
||
{
|
||
// Insufficient buffer
|
||
SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
||
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||
}
|
||
else
|
||
{
|
||
CopyMemory( pchBuffer, szValue, cchValue );
|
||
}
|
||
*pcchRequired = cchValue;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// In the event that HTTP_REQUEST::GetInfoForName
|
||
// failed because of missing data the error code
|
||
// returned is stored in dwOffset.
|
||
//
|
||
DBG_ASSERT( FAILED(dwOffset) );
|
||
|
||
// Rely on BoolFromHresult to do the SetLastError...
|
||
hr = dwOffset;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if( !fCacheHit )
|
||
{
|
||
// Moved from isplocal.cxx - GetServerVariable()
|
||
|
||
HANDLE hCurrentUser = NULL;
|
||
|
||
if( !m_fInProcess )
|
||
{
|
||
hCurrentUser = INVALID_HANDLE_VALUE;
|
||
}
|
||
|
||
DoRevertHack( &hCurrentUser );
|
||
|
||
hr = pIWamRequest->GetInfoForName( szVarName,
|
||
pchBuffer,
|
||
cchBuffer,
|
||
pcchRequired
|
||
);
|
||
|
||
UndoRevertHack( &hCurrentUser );
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
|
||
/*---------------------------------------------------------------------*
|
||
WAM_EXEC_INFO::TransmitFile
|
||
|
||
This function transmits the file contents as specified in the
|
||
pHseTfi. It also sets up the call back functions for
|
||
processing the request when it completes.
|
||
|
||
Arguments:
|
||
pHseTfi - pointer to Server Extension Transmit File information
|
||
|
||
Returns:
|
||
TRUE on success and FALSE on failure
|
||
|
||
*/
|
||
BOOL
|
||
WAM_EXEC_INFO::TransmitFile(
|
||
IN LPHSE_TF_INFO pHseTfi
|
||
)
|
||
{
|
||
|
||
BOOL fReturn = FALSE;
|
||
IWamRequest * pIWamRequest = NULL;
|
||
|
||
|
||
IF_DEBUG( WAM_ISA_CALLS ) {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT
|
||
, "WAM_EXEC_INFO(%p)::TransmitFile "
|
||
"pHseTfi(%p) "
|
||
"\n"
|
||
, this
|
||
, pHseTfi
|
||
));
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// It is unlikely that ISAPI applications will post
|
||
// multiple outstanding IOs. However we have the state
|
||
// pAsyncIoInfo->_fOutstandingIO to secure ourselves against this.
|
||
// I have not used any critical sections to protect against
|
||
// multiple threads for performance reasons.
|
||
// Today we support only Async IO transfers
|
||
//
|
||
|
||
if ( pHseTfi == NULL
|
||
|| _AsyncIoInfo._dwOutstandingIO
|
||
|| ((pHseTfi->dwFlags & HSE_IO_ASYNC) == 0) ) {
|
||
|
||
SetLastError( ERROR_INVALID_PARAMETER );
|
||
return ( FALSE );
|
||
}
|
||
|
||
|
||
if ( pHseTfi->hFile == INVALID_HANDLE_VALUE) {
|
||
|
||
SetLastError( ERROR_INVALID_HANDLE );
|
||
return ( FALSE );
|
||
}
|
||
|
||
//
|
||
// If there is no file being transfered, then having a non-zero
|
||
// offset or BytesToWrite will be bad.
|
||
//
|
||
if( (pHseTfi->hFile == NULL) &&
|
||
(pHseTfi->BytesToWrite != 0 || pHseTfi->Offset != 0) )
|
||
{
|
||
// Consider: We could just set these to 0, ie ignore them
|
||
// if the hFile is NULL.
|
||
|
||
SetLastError( ERROR_INVALID_PARAMETER );
|
||
return ( FALSE );
|
||
}
|
||
|
||
|
||
//
|
||
// Record the number of bytes to complete the IO with
|
||
//
|
||
|
||
if ( pHseTfi->BytesToWrite > 0 ) {
|
||
|
||
DBG_ASSERT( pHseTfi->hFile );
|
||
|
||
_AsyncIoInfo._cbLastAsyncIO = pHseTfi->BytesToWrite;
|
||
|
||
} else {
|
||
|
||
//
|
||
// If a zero-size was passed in, get size from file
|
||
//
|
||
|
||
if( pHseTfi->hFile )
|
||
{
|
||
|
||
BY_HANDLE_FILE_INFORMATION hfi;
|
||
|
||
if ( !GetFileInformationByHandle( pHseTfi->hFile, &hfi )) {
|
||
|
||
// CONSIDER something besides ERROR_INVALID_HANDLE???
|
||
SetLastError( ERROR_INVALID_HANDLE );
|
||
return ( FALSE );
|
||
}
|
||
|
||
if ( hfi.nFileSizeHigh ) {
|
||
|
||
SetLastError( ERROR_NOT_SUPPORTED );
|
||
return ( FALSE );
|
||
}
|
||
|
||
_AsyncIoInfo._cbLastAsyncIO = hfi.nFileSizeLow;
|
||
}
|
||
else
|
||
{
|
||
// We want to allow TransmitFile without a file handle
|
||
_AsyncIoInfo._cbLastAsyncIO = 0;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Set the callback function. Override old one
|
||
//
|
||
|
||
if ( pHseTfi->pfnHseIO != NULL) {
|
||
|
||
_AsyncIoInfo._pfnHseIO = pHseTfi->pfnHseIO;
|
||
}
|
||
|
||
|
||
if ( NULL == _AsyncIoInfo._pfnHseIO) {
|
||
|
||
// No callback specified. return error
|
||
SetLastError( ERROR_INVALID_PARAMETER );
|
||
return ( FALSE );
|
||
}
|
||
|
||
|
||
if ( pHseTfi->pContext != NULL) {
|
||
|
||
// Override the old context
|
||
_AsyncIoInfo._pvHseIOContext = pHseTfi->pContext;
|
||
}
|
||
|
||
|
||
if ( FAILED( GetIWamRequest( &pIWamRequest ) ) ) {
|
||
|
||
// CONSIDER something besides ERROR_INVALID_FUNCTION???
|
||
SetLastError( ERROR_INVALID_FUNCTION );
|
||
return FALSE;
|
||
}
|
||
|
||
DBG_ASSERT( pIWamRequest );
|
||
|
||
|
||
//
|
||
// Finally, call appropriate transmit-file version
|
||
// based on in-proc vs. oop.
|
||
//
|
||
// First, we init async i/o processing. In normal (success) case,
|
||
// i/o completion thread will call balancing uninit. In failure
|
||
// case, we call it below.
|
||
//
|
||
|
||
InitAsyncIO( ASYNC_IO_TYPE_WRITE );
|
||
|
||
|
||
if ( m_fInProcess ) {
|
||
|
||
//
|
||
// call in-proc interface (fastest)
|
||
//
|
||
|
||
fReturn = BoolFromHresult(
|
||
pIWamRequest->TransmitFileInProc(
|
||
#ifdef _WIN64
|
||
(UINT64) this
|
||
#else
|
||
(ULONG_PTR) this
|
||
#endif
|
||
, (unsigned char *) pHseTfi
|
||
) );
|
||
|
||
} else {
|
||
|
||
//
|
||
// call out-of-proc interface
|
||
//
|
||
|
||
unsigned char * pszStatusCode = NULL;
|
||
DWORD cbStatusCode = 0;
|
||
|
||
//
|
||
// if send-headers flag is set, get status code from struct
|
||
//
|
||
|
||
if ( pHseTfi->dwFlags & HSE_IO_SEND_HEADERS ) {
|
||
|
||
DBG_ASSERT( pHseTfi->pszStatusCode );
|
||
|
||
pszStatusCode = (unsigned char *) pHseTfi->pszStatusCode;
|
||
cbStatusCode = lstrlen( pHseTfi->pszStatusCode ) + 1;
|
||
}
|
||
|
||
HANDLE hCurrentUser = INVALID_HANDLE_VALUE;
|
||
DoRevertHack( &hCurrentUser );
|
||
|
||
fReturn = BoolFromHresult(
|
||
pIWamRequest->TransmitFileOutProc(
|
||
#ifdef _WIN64
|
||
(UINT64) this
|
||
, (UINT64) pHseTfi->hFile
|
||
#else
|
||
(ULONG_PTR) this
|
||
, (ULONG_PTR) pHseTfi->hFile
|
||
#endif
|
||
, pszStatusCode
|
||
, cbStatusCode
|
||
, pHseTfi->BytesToWrite
|
||
, pHseTfi->Offset
|
||
, (unsigned char *) pHseTfi->pHead
|
||
, pHseTfi->HeadLength
|
||
, (unsigned char *) pHseTfi->pTail
|
||
, pHseTfi->TailLength
|
||
, pHseTfi->dwFlags
|
||
) );
|
||
|
||
UndoRevertHack( &hCurrentUser );
|
||
|
||
}
|
||
|
||
|
||
if ( !fReturn ) {
|
||
|
||
UninitAsyncIO();
|
||
}
|
||
|
||
|
||
ReleaseIWamRequest( pIWamRequest );
|
||
|
||
|
||
return fReturn;
|
||
|
||
} // WAM_EXEC_INFO::TransmitFile()
|
||
|
||
|
||
|
||
|
||
/*---------------------------------------------------------------------*
|
||
WAM_EXEC_INFO::AsyncReadClient
|
||
|
||
Description:
|
||
|
||
This function performs an async read of client (browser) data
|
||
on behalf of the ISA
|
||
|
||
Arguments:
|
||
|
||
pvBuff - Data buffer to read into
|
||
pcbToRead - Number of bytes to read, set to number of bytes read if
|
||
request is not Async
|
||
dwFlags - Receive flags
|
||
|
||
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.
|
||
|
||
Returns:
|
||
|
||
TRUE on success and FALSE on failure
|
||
|
||
*/
|
||
BOOL
|
||
WAM_EXEC_INFO::AsyncReadClient(
|
||
IN OUT PVOID pvBuff
|
||
, IN OUT DWORD *pcbToRead
|
||
, IN DWORD dwFlags
|
||
)
|
||
{
|
||
BOOL fReturn = FALSE;
|
||
|
||
DBG_ASSERT( pvBuff );
|
||
DBG_ASSERT( pcbToRead );
|
||
|
||
IF_DEBUG( WAM_ISA_CALLS ) {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT
|
||
, "WAM_EXEC_INFO(%p)::AsyncReadClient\t"
|
||
"Bytes to read = %d\n"
|
||
, this
|
||
, *pcbToRead
|
||
));
|
||
}
|
||
|
||
|
||
DBG_ASSERT( dwFlags & HSE_IO_ASYNC );
|
||
|
||
//
|
||
// It is unlikely that ISAPI applications will post
|
||
// multiple outstanding IOs. However we have the state
|
||
// _AsyncIoInfo._dwOutstandingIO to secure ourselves against such cases.
|
||
// I have not used any critical sections to protect against
|
||
// multiple threads for performance reasons.
|
||
// Today we support only Async IO transfers
|
||
//
|
||
|
||
|
||
if ( _AsyncIoInfo._dwOutstandingIO ||
|
||
((dwFlags & HSE_IO_ASYNC) == 0) ) {
|
||
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return ( FALSE);
|
||
}
|
||
|
||
if ( NULL == _AsyncIoInfo._pfnHseIO) {
|
||
|
||
// No callback specified. return error
|
||
SetLastError( ERROR_INVALID_PARAMETER);
|
||
return (FALSE);
|
||
}
|
||
|
||
//
|
||
// Setup stage for and execute AsyncReadClient operation
|
||
//
|
||
|
||
//
|
||
// If callback function exists and flags indicate Async IO, do it.
|
||
// Also there should be no outstanding Async IO operation.
|
||
//
|
||
|
||
if ( dwFlags & HSE_IO_ASYNC) {
|
||
|
||
//
|
||
// 1. Set Request state to be async IO from ISAPI client
|
||
// 2. Submit Async IOP
|
||
// 3. return to the ISAPI application
|
||
//
|
||
|
||
IWamRequest * pIWamRequest = NULL;
|
||
|
||
if ( FAILED( GetIWamRequest( &pIWamRequest ) ) ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
InitAsyncIO( ASYNC_IO_TYPE_READ );
|
||
|
||
if( m_fInProcess )
|
||
{
|
||
fReturn = BoolFromHresult( pIWamRequest->AsyncReadClientExt(
|
||
#ifdef _WIN64
|
||
(UINT64) this
|
||
#else
|
||
(ULONG_PTR) this
|
||
#endif
|
||
, (unsigned char *) pvBuff
|
||
, *pcbToRead
|
||
) );
|
||
}
|
||
else
|
||
{
|
||
DBG_ASSERT( _AsyncIoInfo._pvAsyncReadBuffer == NULL );
|
||
_AsyncIoInfo._pvAsyncReadBuffer = pvBuff;
|
||
|
||
fReturn = BoolFromHresult( pIWamRequest->AsyncReadClientOop(
|
||
#ifdef _WIN64
|
||
(UINT64) this
|
||
#else
|
||
(ULONG_PTR) this
|
||
#endif
|
||
, *pcbToRead
|
||
) );
|
||
if( !fReturn )
|
||
{
|
||
_AsyncIoInfo._pvAsyncReadBuffer = NULL;
|
||
}
|
||
}
|
||
|
||
ReleaseIWamRequest( pIWamRequest );
|
||
|
||
|
||
if ( !fReturn ) {
|
||
UninitAsyncIO();
|
||
}
|
||
|
||
} else {
|
||
|
||
DBG_ASSERT( FALSE );
|
||
|
||
}
|
||
|
||
return ( fReturn);
|
||
} // WAM_EXEC_INFO::AsyncReadClient()
|
||
|
||
|
||
|
||
|
||
/*---------------------------------------------------------------------*
|
||
WAM_EXEC_INFO::ProcessAsyncIO
|
||
|
||
Description:
|
||
|
||
Completes an async i/o by calling the ISA's i/o completion callback
|
||
|
||
Arguments:
|
||
|
||
Returns:
|
||
BOOL
|
||
|
||
*/
|
||
BOOL
|
||
WAM_EXEC_INFO::ProcessAsyncIO(
|
||
DWORD dwStatus
|
||
, DWORD cbWritten
|
||
)
|
||
{
|
||
|
||
DBG_ASSERT( _AsyncIoInfo._pfnHseIO );
|
||
DBG_ASSERT( _AsyncIoInfo._dwOutstandingIO );
|
||
|
||
IF_DEBUG( WAM_ISA_CALLS ) {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"WAM_EXEC_INFO[%p]::ProcessAsyncIO(IOStatus=%d, %d bytes)\n"
|
||
, this, dwStatus, cbWritten
|
||
));
|
||
}
|
||
|
||
|
||
BOOL fRet = TRUE;
|
||
BOOL fImpersonated = FALSE;
|
||
DWORD dwIOType = ASYNC_IO_TYPE_NONE;
|
||
|
||
//
|
||
// 1.
|
||
// We are in the return from an async io completion.
|
||
// We will be making a call into the ISAPI DLL to notify that
|
||
// the IO operation completed.
|
||
// This callback has to occur within the bounds of ref/deref
|
||
// of the WAM_EXEC_INFO otherwise following race condition can occur:
|
||
// - isapi's completion function calls done-with-session;
|
||
// if that release got rid of last ref, this WAM_EXEC_INFO
|
||
// gets destroyed
|
||
// - isapi's completion function continues doing other work
|
||
// - an unrelated shutdown command comes in asynchronously
|
||
// - if isapi has no TerminateExtension (or a non-robust one),
|
||
// isapi gets summarily unloaded
|
||
// - any of a number of terrible things can happen
|
||
//
|
||
// If you HAVE temptations to optimize this,
|
||
// please first talk to MuraliK
|
||
//
|
||
AddRef();
|
||
|
||
//
|
||
// 2.
|
||
// Un-init async i/o - balances init we did before requesting i/o
|
||
//
|
||
// NOTE we do this before calling the isapi's completion function
|
||
// Reasons:
|
||
// WAM_EXEC_INFO maintians state that an async IO operation is going on
|
||
// UninitAsyncIO resets this state and other associated ref-counts.
|
||
// This way we ensure that any further async IO callbacks made during
|
||
// the call to the ISAPI DLL will be honored properly.
|
||
//
|
||
|
||
// Only call this in success case.
|
||
if (dwStatus == 0)
|
||
{
|
||
dwIOType = _AsyncIoInfo._dwOutstandingIO;
|
||
UninitAsyncIO();
|
||
}
|
||
|
||
//
|
||
// 3.
|
||
// Impersonate before making ISAPI callback
|
||
//
|
||
|
||
if ( !m_pWam->FWin95() )
|
||
{
|
||
HANDLE hToken = _WamReqCore.m_WamReqCoreFixed.m_hUserToken;
|
||
|
||
if ( !( fImpersonated = ImpersonateLoggedOnUser( hToken ) ) )
|
||
{
|
||
|
||
DBGPRINTF((DBG_CONTEXT,
|
||
"WAM_EXEC_INFO(%p) ImpersonateLoggedOnUser(%x)"
|
||
"failed[err %d]\n",
|
||
this, hToken, GetLastError()));
|
||
|
||
fRet = FALSE;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// 4.
|
||
// call isapi if we are successful so far
|
||
//
|
||
|
||
if ( fRet ) {
|
||
|
||
__try {
|
||
|
||
//
|
||
// Adjust bytes written for async writes -
|
||
// otherwise filter adjusted bytes show up
|
||
// which confuses some ISAPI Applications
|
||
//
|
||
|
||
if ( dwIOType == ASYNC_IO_TYPE_WRITE
|
||
&& dwStatus == ERROR_SUCCESS ) {
|
||
|
||
cbWritten = _AsyncIoInfo._cbLastAsyncIO;
|
||
|
||
_AsyncIoInfo._cbLastAsyncIO = 0;
|
||
}
|
||
|
||
|
||
//
|
||
// Make the ISAPI callback to indicate that the I/O completed
|
||
//
|
||
|
||
(*_AsyncIoInfo._pfnHseIO)(
|
||
&ecb,
|
||
_AsyncIoInfo._pvHseIOContext,
|
||
cbWritten,
|
||
dwStatus
|
||
);
|
||
|
||
}
|
||
__except ( g_fEnableTryExcept ?
|
||
WAMExceptionFilter( GetExceptionInformation(),
|
||
WAM_EVENT_EXTENSION_EXCEPTION,
|
||
this ) :
|
||
EXCEPTION_CONTINUE_SEARCH )
|
||
{
|
||
fRet = FALSE;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// 5.
|
||
// Revert back if we were impersonated before making ISAPI DLL callback
|
||
//
|
||
|
||
if ( fImpersonated )
|
||
{
|
||
::RevertToSelf( );
|
||
fImpersonated = FALSE;
|
||
}
|
||
|
||
if (dwStatus != 0)
|
||
{
|
||
UninitAsyncIO();
|
||
}
|
||
//
|
||
// Complimentary release for the AddRef() done in Step (1) above
|
||
//
|
||
Release();
|
||
|
||
|
||
return fRet;
|
||
|
||
} // ProcessAsyncIO()
|
||
|
||
BOOL
|
||
WAM_EXEC_INFO::ProcessAsyncReadOop
|
||
(
|
||
DWORD dwStatus,
|
||
DWORD cbRead,
|
||
unsigned char * lpDataRead
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
Handle callback for out of process AsyncRead
|
||
|
||
Arguments:
|
||
|
||
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( !m_fInProcess );
|
||
DBG_ASSERT( _AsyncIoInfo._pvAsyncReadBuffer != NULL );
|
||
|
||
// Copy the marshalled data into the client's buffer.
|
||
|
||
if( dwStatus == STATUS_SUCCESS )
|
||
{
|
||
//
|
||
// Is there more we can do to protect this?
|
||
//
|
||
// We'd have problems anyway if the client's buffer wasn't large
|
||
// enough to hold the data and cbRead should always be <= than the
|
||
// size the client specified.
|
||
//
|
||
CopyMemory( _AsyncIoInfo._pvAsyncReadBuffer, lpDataRead, cbRead );
|
||
}
|
||
|
||
_AsyncIoInfo._pvAsyncReadBuffer = NULL;
|
||
return ProcessAsyncIO( dwStatus, cbRead );
|
||
}
|
||
|
||
|
||
|
||
/*---------------------------------------------------------------------*
|
||
WAM_EXEC_INFO::InitAsyncIO
|
||
Initilializes members before requesting async i/o
|
||
|
||
Arguments:
|
||
None
|
||
|
||
Returns:
|
||
Nothing
|
||
|
||
*/
|
||
VOID
|
||
WAM_EXEC_INFO::InitAsyncIO( DWORD dwIOType )
|
||
{
|
||
|
||
IF_DEBUG( WAM_ISA_CALLS ) {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT
|
||
, "WAM_EXEC_INFO(%p)::InitAsyncIO "
|
||
"\n"
|
||
, this
|
||
));
|
||
|
||
}
|
||
|
||
DBG_ASSERT( dwIOType == ASYNC_IO_TYPE_READ || dwIOType == ASYNC_IO_TYPE_WRITE );
|
||
DBG_ASSERT( _AsyncIoInfo._dwOutstandingIO == ASYNC_IO_TYPE_NONE );
|
||
DBG_ASSERT( _AsyncIoInfo._pfnHseIO != NULL);
|
||
DBG_ASSERT( _AsyncIoInfo._pvAsyncReadBuffer == NULL );
|
||
|
||
|
||
AddRef();
|
||
_AsyncIoInfo._dwOutstandingIO = dwIOType;
|
||
}
|
||
|
||
|
||
|
||
|
||
/*---------------------------------------------------------------------*
|
||
WAM_EXEC_INFO::UninitAsyncIO
|
||
Un-initilializes members before completing async i/o
|
||
|
||
Arguments:
|
||
None
|
||
|
||
Returns:
|
||
Nothing
|
||
|
||
*/
|
||
VOID
|
||
WAM_EXEC_INFO::UninitAsyncIO()
|
||
{
|
||
|
||
IF_DEBUG( WAM_ISA_CALLS ) {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT
|
||
, "WAM_EXEC_INFO(%p)::UninitAsyncIO "
|
||
"\n"
|
||
, this
|
||
));
|
||
|
||
}
|
||
|
||
|
||
_AsyncIoInfo._dwOutstandingIO = ASYNC_IO_TYPE_NONE;
|
||
Release();
|
||
|
||
}
|
||
|
||
|
||
/*---------------------------------------------------------------------*
|
||
WAM_EXEC_INFO::IsValid
|
||
Is this a valid object?
|
||
|
||
Arguments:
|
||
None
|
||
|
||
Returns:
|
||
BOOL
|
||
|
||
*/
|
||
BOOL
|
||
WAM_EXEC_INFO::IsValid( )
|
||
{
|
||
//
|
||
// CONSIDER more thorough error checking
|
||
//
|
||
return (m_dwSignature == WAM_EXEC_INFO_SIGNATURE);
|
||
}
|
||
|
||
|
||
/*---------------------------------------------------------------------*
|
||
WAM_EXEC_INFO::AddRef
|
||
Add refs
|
||
|
||
Arguments:
|
||
None
|
||
|
||
Returns:
|
||
Ref count
|
||
|
||
*/
|
||
ULONG
|
||
WAM_EXEC_INFO::AddRef( )
|
||
{
|
||
|
||
IF_DEBUG( WAM_REFCOUNTS ) {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT
|
||
, "WAM_EXEC_INFO(%p) AddRef : %d -> %d\n"
|
||
, this
|
||
, _cRefs
|
||
, _cRefs + 1
|
||
));
|
||
}
|
||
|
||
|
||
LONG cRefs = InterlockedIncrement( &_cRefs );
|
||
|
||
|
||
#if DBG
|
||
|
||
//
|
||
// Write to both this request's trace log and global trace log
|
||
//
|
||
|
||
if( m_pDbgRefTraceLog != NULL ) {
|
||
|
||
WriteRefTraceLog(
|
||
m_pDbgRefTraceLog
|
||
, cRefs
|
||
, (PVOID) this
|
||
);
|
||
}
|
||
|
||
if( sm_pDbgRefTraceLog != NULL ) {
|
||
|
||
WriteRefTraceLog(
|
||
sm_pDbgRefTraceLog
|
||
, cRefs
|
||
, (PVOID) this
|
||
);
|
||
}
|
||
|
||
#endif
|
||
|
||
return cRefs;
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
/*---------------------------------------------------------------------*
|
||
WAM_EXEC_INFO::CleanupAndRelease
|
||
|
||
Description:
|
||
Calls wamreq's (prep)cleanup, then releases this wamexecinfo.
|
||
|
||
Arguments:
|
||
None
|
||
|
||
Returns:
|
||
Nothing
|
||
|
||
*/
|
||
void
|
||
WAM_EXEC_INFO::CleanupAndRelease(
|
||
BOOL fFullCleanup
|
||
)
|
||
{
|
||
|
||
//
|
||
// Prep wamreq's cleanup
|
||
//
|
||
// This method should only be called from the IIS thread
|
||
//
|
||
|
||
DBG_ASSERT( m_dwThreadIdIIS == GetCurrentThreadId() );
|
||
|
||
//
|
||
// While on IIS thread m_pIWamReqIIS must be a valid pointer
|
||
//
|
||
|
||
DBG_ASSERT( m_pIWamReqIIS );
|
||
|
||
//
|
||
// The skip wamreq cleanup is only set by ASP after returning
|
||
// a status pending
|
||
//
|
||
|
||
//
|
||
// init hr's to failure - these will drive cleanup logic below,
|
||
// but only if calls are successful
|
||
//
|
||
|
||
HRESULT hrCoInitEx = E_FAIL;
|
||
HRESULT hrGetIWamReq = E_FAIL;
|
||
IWamRequest * pIWamRequest = NULL;
|
||
|
||
|
||
if ( m_fInProcess ) {
|
||
|
||
//
|
||
// inproc case, use our cached ptr.
|
||
|
||
pIWamRequest = m_pIWamReqIIS;
|
||
|
||
} else {
|
||
|
||
|
||
//
|
||
// in oop case,
|
||
// an isapi might have changed the thread's mode on us
|
||
// (for example, by coinit'ing single-threaded).
|
||
// if so, we need to get an interface pointer from gip,
|
||
// since our cached ptr will no longer be valid.
|
||
//
|
||
// NOTE we test this by calling coinit, which is cheap.
|
||
// if this succeeds, we plough ahead with our cached ptr.
|
||
// else if mode changed, we get an interface ptr from gip.
|
||
//
|
||
|
||
hrCoInitEx = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||
|
||
|
||
if ( hrCoInitEx == RPC_E_CHANGED_MODE ) {
|
||
|
||
hrGetIWamReq = GetIWamRequest( &pIWamRequest );
|
||
|
||
} else {
|
||
|
||
//
|
||
// NOTE in most cases we are here because we succeeded
|
||
// in others we forge ahead and hope for the best ...
|
||
// at any rate, we cannot be worse off than before
|
||
// we added the above co-init call
|
||
//
|
||
|
||
pIWamRequest = m_pIWamReqIIS;
|
||
|
||
}
|
||
|
||
|
||
}
|
||
|
||
|
||
|
||
if ( pIWamRequest != NULL ) {
|
||
|
||
HANDLE hCurrentUser = m_fInProcess ? NULL : INVALID_HANDLE_VALUE;
|
||
DoRevertHack( &hCurrentUser );
|
||
|
||
if ( fFullCleanup ) {
|
||
|
||
//
|
||
// we are doing full cleanup
|
||
//
|
||
|
||
pIWamRequest->CleanupWamRequest(
|
||
(unsigned char*) ecb.lpszLogData
|
||
, lstrlen( ecb.lpszLogData ) + 1
|
||
, ecb.dwHttpStatusCode
|
||
, _dwIsaKeepConn
|
||
);
|
||
|
||
} else {
|
||
|
||
//
|
||
// we are not doing full cleanup, so call 'Prep' only
|
||
//
|
||
|
||
pIWamRequest->PrepCleanupWamRequest(
|
||
(unsigned char*) ecb.lpszLogData
|
||
, lstrlen( ecb.lpszLogData ) + 1
|
||
, ecb.dwHttpStatusCode
|
||
, _dwIsaKeepConn
|
||
);
|
||
|
||
}
|
||
|
||
UndoRevertHack( &hCurrentUser );
|
||
}
|
||
|
||
|
||
//
|
||
// if we got a ptr from gip, release it
|
||
//
|
||
|
||
if ( SUCCEEDED( hrGetIWamReq ) ) {
|
||
|
||
ReleaseIWamRequest( pIWamRequest );
|
||
}
|
||
|
||
|
||
//
|
||
// if we co-init'ed, co-uninit
|
||
//
|
||
|
||
if ( hrCoInitEx == S_OK ) {
|
||
|
||
CoUninitialize( );
|
||
}
|
||
|
||
|
||
//
|
||
// Release this
|
||
//
|
||
|
||
Release( );
|
||
|
||
|
||
return;
|
||
|
||
} // CleanupAndRelease
|
||
|
||
|
||
|
||
|
||
/*---------------------------------------------------------------------*
|
||
WAM_EXEC_INFO::Release
|
||
Releases
|
||
|
||
Arguments:
|
||
None
|
||
|
||
Returns:
|
||
Ref count
|
||
|
||
*/
|
||
ULONG
|
||
WAM_EXEC_INFO::Release(
|
||
)
|
||
|
||
{
|
||
|
||
IF_DEBUG( WAM_REFCOUNTS ) {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT
|
||
, "WAM_EXEC_INFO(%p) Release: %d -> %d\n"
|
||
, this
|
||
, _cRefs
|
||
, _cRefs-1
|
||
));
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Write the trace log BEFORE the decrement operation :(
|
||
// If we write it after the decrement, we will run into potential
|
||
// race conditions in this object getting freed up accidentally
|
||
// by another thread
|
||
//
|
||
|
||
#if DBG
|
||
|
||
//
|
||
// Write to both this request's trace log and global trace log
|
||
//
|
||
|
||
if( m_pDbgRefTraceLog != NULL ) {
|
||
|
||
WriteRefTraceLog(
|
||
m_pDbgRefTraceLog
|
||
, _cRefs - 1 // ref count AFTER decrement happens
|
||
, (PVOID) this
|
||
);
|
||
}
|
||
|
||
if( sm_pDbgRefTraceLog != NULL ) {
|
||
|
||
WriteRefTraceLog(
|
||
sm_pDbgRefTraceLog
|
||
, _cRefs - 1 // ref count AFTER decrement happens
|
||
, (PVOID) this
|
||
);
|
||
}
|
||
|
||
#endif
|
||
|
||
|
||
LONG cRefs = InterlockedDecrement( &_cRefs );
|
||
|
||
|
||
if( cRefs == 0) {
|
||
|
||
IF_DEBUG( WAM_REFCOUNTS ) {
|
||
|
||
DBGPRINTF(( DBG_CONTEXT, "... dying ...\n\n" ));
|
||
}
|
||
|
||
|
||
CleanupWamExecInfo( );
|
||
|
||
//
|
||
// Finally, delete ourselves.
|
||
//
|
||
|
||
delete this;
|
||
return 0;
|
||
|
||
}
|
||
|
||
return cRefs;
|
||
}
|
||
|
||
|
||
|
||
|
||
/*---------------------------------------------------------------------*
|
||
WAM_EXEC_INFO::CleanupWamExecInfo
|
||
Cleans up this object prior to its destruction
|
||
|
||
Arguments:
|
||
None
|
||
|
||
Returns:
|
||
Nothing
|
||
|
||
*/
|
||
VOID
|
||
WAM_EXEC_INFO::CleanupWamExecInfo(
|
||
)
|
||
|
||
{
|
||
|
||
//
|
||
// remove this from its list
|
||
//
|
||
|
||
m_pWam->RemoveFromList( &_ListEntry);
|
||
|
||
|
||
if ( !m_fInProcess & !(m_pWam->FWin95()) ) {
|
||
|
||
//
|
||
// If oop, close the impersonation token
|
||
// (dup'ed in w3svc!HGetOopImpersonationToken)
|
||
//
|
||
// NOTE ignore if in-proc or win95 because we never dup'ed
|
||
// handle in the first place
|
||
//
|
||
|
||
DBG_ASSERT( _WamReqCore.m_WamReqCoreFixed.m_hUserToken
|
||
!= (HANDLE)0 );
|
||
|
||
CloseHandle( _WamReqCore.m_WamReqCoreFixed.m_hUserToken );
|
||
_WamReqCore.m_WamReqCoreFixed.m_hUserToken = (HANDLE)0;
|
||
}
|
||
|
||
if ( _psExtension != NULL) {
|
||
// release the extension object
|
||
g_psextensions->ReleaseExtension( _psExtension);
|
||
_psExtension = NULL;
|
||
}
|
||
|
||
|
||
DBG_ASSERT( QueryPWam());
|
||
QueryPWam()->QueryWamStats().DecrCurrentWamRequests();
|
||
|
||
|
||
CleanupWamExecBase();
|
||
|
||
|
||
return;
|
||
|
||
} // WAM_EXEC_INFO::CleanupWamExecInfo
|
||
|
||
|
||
|
||
|
||
/*---------------------------------------------------------------------*
|
||
WAM_EXEC_INFO::ISAThreadNotify
|
||
Notifies WAM_EXEC_BASE that an ISAPI thread is about to
|
||
start/stop using it. Allows to cache IWamRequest* in the
|
||
OOP case.
|
||
|
||
NOTE this method is on WAM_EXEC_INFO (rather than WAM_EXEC_BASE)
|
||
because it must addref and release.
|
||
|
||
Arguments:
|
||
fStart thread start (TRUE) / thread end (FALSE)
|
||
|
||
Returns:
|
||
HRESULT
|
||
|
||
*/
|
||
HRESULT
|
||
WAM_EXEC_INFO::ISAThreadNotify(
|
||
BOOL fStart
|
||
)
|
||
{
|
||
|
||
if ( m_fInProcess ) {
|
||
|
||
//
|
||
// In-proc: no-op
|
||
//
|
||
|
||
return NOERROR;
|
||
|
||
}
|
||
|
||
IF_DEBUG( WAM_THREADID ) {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT
|
||
, "WAM_EXEC_INFO(%p)::ISAThreadNotify(%d) Thread(%d)\n"
|
||
, this
|
||
, fStart
|
||
, GetCurrentThreadId()
|
||
));
|
||
}
|
||
|
||
HRESULT hr = NOERROR;
|
||
|
||
if ( fStart ) {
|
||
|
||
//
|
||
// Out-of-proc: when starting the ISA's single-thread
|
||
// sequence, cache ISA-thread ptr we get from gip-master.
|
||
//
|
||
|
||
if ( SUCCEEDED( hr = GetInterfaceForThread( ) ) ) {
|
||
|
||
AddRef();
|
||
}
|
||
|
||
DBG_ASSERT( AssertSmartISAValid() || (hr != NOERROR) );
|
||
|
||
|
||
} else {
|
||
|
||
//
|
||
// Out-of-proc: when ending the ISA's single-thread
|
||
// sequence, release ISA-thread ptr
|
||
//
|
||
|
||
hr = ReleaseInterfaceForThread( );
|
||
|
||
Release();
|
||
|
||
}
|
||
|
||
return hr;
|
||
|
||
} // WAM_EXEC_INFO::ISAThreadNotify
|
||
|
||
|
||
/*---------------------------------------------------------------------*
|
||
Debug methods
|
||
|
||
*/
|
||
|
||
#if DBG
|
||
|
||
VOID
|
||
WAM_EXEC_INFO::Print( VOID) const
|
||
{
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT
|
||
, "WAM_EXEC_INFO(%p): Method: %s; Query: %s;\n"
|
||
"PathInfo: %s; PathTrans: %s; ContentType: %s;\n"
|
||
"URL: %s; ISA DLL path: %s;\n"
|
||
"In-Proc = %d; m_pIWamReqInproc = %p; "
|
||
"m_gipIWamRequest = %p; m_pIWamReqSmartISA = %p\n"
|
||
"Flags = %x; ChildExecFlags = %x; RefCount = %d\n"
|
||
"Extension = %p; OutstandingIO = %d; "
|
||
"IoCompletion() = %p; IoContext = %p\n"
|
||
, this
|
||
, _WamReqCore.GetSz( WRC_I_METHOD )
|
||
, _WamReqCore.GetSz( WRC_I_QUERY )
|
||
, _WamReqCore.GetSz( WRC_I_PATHINFO )
|
||
, _WamReqCore.GetSz( WRC_I_PATHTRANS )
|
||
, _WamReqCore.GetSz( WRC_I_CONTENTTYPE )
|
||
, _WamReqCore.GetSz( WRC_I_URL )
|
||
, _WamReqCore.GetSz( WRC_I_ISADLLPATH )
|
||
, m_fInProcess
|
||
, m_pIWamReqInproc
|
||
, m_gipIWamRequest
|
||
, m_pIWamReqSmartISA
|
||
, _dwFlags
|
||
, _dwChildExecFlags
|
||
, _cRefs
|
||
, _psExtension
|
||
, _AsyncIoInfo._dwOutstandingIO
|
||
, _AsyncIoInfo._pfnHseIO
|
||
, _AsyncIoInfo._pvHseIOContext
|
||
));
|
||
|
||
return;
|
||
} // WAM_EXEC_INFO::Print()
|
||
|
||
#else
|
||
|
||
VOID WAM_EXEC_INFO::Print( VOID) const { }
|
||
|
||
#endif //DBG
|
||
|
||
|
||
|
||
#if DBG
|
||
|
||
void
|
||
DbgWamreqRefcounts
|
||
(
|
||
char* szPrefix,
|
||
WAM_EXEC_INFO * pWamExecInfo,
|
||
long cRefsWamRequest,
|
||
long cRefsWamReqContext
|
||
)
|
||
{
|
||
|
||
IWamRequest * pIWamRequest = NULL;
|
||
pWamExecInfo->GetIWamRequest( &pIWamRequest );
|
||
DBG_ASSERT( pIWamRequest );
|
||
|
||
IF_DEBUG( WAM_REFCOUNTS ) {
|
||
DBGPRINTF(( DBG_CONTEXT, szPrefix ));
|
||
DBGPRINTF(( DBG_CONTEXT, "\n" ));
|
||
}
|
||
|
||
HANDLE hCurrentUser = pWamExecInfo->FInProcess() ? NULL : INVALID_HANDLE_VALUE;
|
||
DoRevertHack( &hCurrentUser );
|
||
|
||
if( cRefsWamRequest != -1) {
|
||
|
||
DBG_ASSERT( cRefsWamRequest
|
||
== (long) pIWamRequest->DbgRefCount() );
|
||
}
|
||
|
||
if( cRefsWamReqContext != -1) {
|
||
DBG_ASSERT( cRefsWamReqContext
|
||
== (long) pWamExecInfo->DbgRefCount() );
|
||
}
|
||
|
||
IF_DEBUG( WAM_REFCOUNTS ) {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT
|
||
, "IWamRequest(%p): RefCount = %d\n"
|
||
, pIWamRequest
|
||
, pIWamRequest->DbgRefCount()
|
||
));
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT
|
||
, "WAM_EXEC_INFO(%p): RefCount = %d\n"
|
||
, pWamExecInfo, pWamExecInfo->DbgRefCount()
|
||
));
|
||
|
||
}
|
||
|
||
UndoRevertHack( &hCurrentUser );
|
||
|
||
pWamExecInfo->ReleaseIWamRequest( pIWamRequest );
|
||
|
||
} // WAM_EXEC_INFO::DbgWamreqRefcounts
|
||
#endif // DBG
|
||
|
||
|
||
/************************************************************
|
||
* Static Member Functions of WAM_EXEC_INFO
|
||
************************************************************/
|
||
|
||
|
||
BOOL
|
||
WAM_EXEC_INFO::InitClass( VOID)
|
||
{
|
||
HRESULT hr;
|
||
|
||
ALLOC_CACHE_CONFIGURATION acConfig = {
|
||
1
|
||
, WAM_EXEC_INFO_CACHE_THRESHOLD
|
||
, sizeof(WAM_EXEC_INFO)
|
||
};
|
||
|
||
if ( NULL != sm_pachExecInfo) {
|
||
|
||
// already initialized
|
||
return ( TRUE );
|
||
}
|
||
|
||
hr = g_GIPAPI.Init();
|
||
if( FAILED( hr ) ) {
|
||
DBGPRINTF( (DBG_CONTEXT, "GIPAPI::Init Failed: %8.8x\n", hr) );
|
||
return ( FALSE );
|
||
}
|
||
|
||
sm_pachExecInfo = new ALLOC_CACHE_HANDLER( "WamExecInfo",
|
||
&acConfig);
|
||
|
||
#if DBG
|
||
sm_pDbgRefTraceLog = CreateRefTraceLog( C_REFTRACES_GLOBAL, 0 );
|
||
#endif
|
||
|
||
return ( NULL != sm_pachExecInfo);
|
||
} // WAM_EXEC_INFO::InitClass()
|
||
|
||
|
||
VOID
|
||
WAM_EXEC_INFO::CleanupClass( VOID)
|
||
{
|
||
HRESULT hr;
|
||
|
||
hr = g_GIPAPI.UnInit();
|
||
|
||
if( FAILED( hr ) ) {
|
||
DBGPRINTF( (DBG_CONTEXT, "GIPAPI::UnInit returned %8.8x\n", hr ) );
|
||
}
|
||
|
||
if ( NULL != sm_pachExecInfo) {
|
||
|
||
delete sm_pachExecInfo;
|
||
sm_pachExecInfo = NULL;
|
||
}
|
||
|
||
#if DBG
|
||
DestroyRefTraceLog( sm_pDbgRefTraceLog );
|
||
#endif
|
||
|
||
return;
|
||
} // WAM_EXEC_INFO::CleanupClass()
|
||
|
||
|
||
void *
|
||
WAM_EXEC_INFO::operator new( size_t s)
|
||
{
|
||
DBG_ASSERT( s == sizeof( WAM_EXEC_INFO));
|
||
|
||
// allocate from allocation cache.
|
||
DBG_ASSERT( NULL != sm_pachExecInfo);
|
||
return (sm_pachExecInfo->Alloc());
|
||
} // WAM_EXEC_INFO::operator new()
|
||
|
||
void
|
||
WAM_EXEC_INFO::operator delete( void * psi)
|
||
{
|
||
DBG_ASSERT( NULL != psi);
|
||
|
||
// free to the allocation pool
|
||
DBG_ASSERT( NULL != sm_pachExecInfo);
|
||
DBG_REQUIRE( sm_pachExecInfo->Free(psi));
|
||
|
||
return;
|
||
} // WAM_EXEC_INFO::operator delete()
|
||
|
||
|
||
|
||
/************************ End of File *********************************/
|