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 *********************************/
|