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