/*===================================================================
Microsoft Denali

Microsoft Confidential.
Copyright 1996 Microsoft Corporation. All Rights Reserved.

Component: Main

File: Hitobj.cpp

Owner: DmitryR


This file contains the CHitObj class implementation.
===================================================================*/
#include "denpre.h"
#pragma hdrstop

#include "context.h"
#include "exec.h"
#include "mtacb.h"
#include "perfdata.h"
#include "debugger.h"
#include "asperror.h"
#include "thrdgate.h"

#include "memchk.h"

//resource failure globals
#include "rfs.h"
#ifdef _RFS
MemRFS memrfs;
#endif


#ifdef SCRIPT_STATS

# define REG_ASP_DEBUG_LOCATION "System\\CurrentControlSet\\Services\\W3Svc\\ASP"

# define REG_STR_QUEUE_DEBUG_THRESHOLD "QueueDebugThreshold"
# define REG_DEF_QUEUE_DEBUG_THRESHOLD 25
DWORD g_dwQueueDebugThreshold = 0; // REG_DEF_QUEUE_DEBUG_THRESHOLD;

# define REG_STR_SEND_SCRIPTLESS_ON_ATQ_THREAD "SendScriptlessOnAtqThread"
# define REG_DEF_SEND_SCRIPTLESS_ON_ATQ_THREAD 1
DWORD g_fSendScriptlessOnAtqThread = REG_DEF_SEND_SCRIPTLESS_ON_ATQ_THREAD;


void
ReadRegistrySettings()
{
    HKEY hkey = NULL;
    DWORD dwErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_ASP_DEBUG_LOCATION,
                               0, KEY_READ, &hkey);
    if (dwErr == NO_ERROR)
    {
        DWORD dwType, dwBuffer;
        DWORD cbBuffer = sizeof(dwBuffer);

        dwErr = RegQueryValueEx(hkey, REG_STR_QUEUE_DEBUG_THRESHOLD,
                                NULL, &dwType, (LPBYTE) &dwBuffer, &cbBuffer);
        if (dwErr == NO_ERROR)
            g_dwQueueDebugThreshold = dwBuffer;

        dwErr = RegQueryValueEx(hkey, REG_STR_SEND_SCRIPTLESS_ON_ATQ_THREAD,
                                NULL, &dwType, (LPBYTE) &dwBuffer, &cbBuffer);
        if (dwErr == NO_ERROR)
            g_fSendScriptlessOnAtqThread = dwBuffer;

        RegCloseKey(hkey);
    }

    char szTemp[200];
    sprintf(szTemp, "RRS, err = %d, QueueDebugThreshold = %d, SendScriptlessOnAtqThread = %d\n",
            dwErr, g_dwQueueDebugThreshold, g_fSendScriptlessOnAtqThread);
    OutputDebugString(szTemp);
}


CSmallSpinLock g_lockRequestStats;
LONG           g_cRequests = 0;
LONG           g_cScriptlessRequests = 0;
LONG           g_cHttpExtensionsExecuting = 0;
LONG           g_cConcurrentScriptlessRequests = 0;
LONG           g_cMaxConcurrentScriptlessRequests = 0;
LONGLONG       g_nSumConcurrentScriptlessRequests = 0;
LONGLONG       g_nSumExecTimeScriptlessRequests = 0;
LONG           g_nAvgConcurrentScriptlessRequests = 0;
LONG           g_nAvgExecTimeScriptlessRequests = 0;

#endif // SCRIPT_STATS

DWORD g_nBrowserRequests = 0;
DWORD g_nSessionCleanupRequests = 0;
DWORD g_nApplnCleanupRequests = 0;

IGlobalInterfaceTable *g_pGIT = NULL;

/*===================================================================
CHitObj::CHitObj

Constructor 

Parameters:
    NONE

Returns:
    NONE
===================================================================*/   
CHitObj::CHitObj()
  : m_fInited(FALSE),
    m_ehtType(ehtUnInitedRequest),
    m_hImpersonate(hNil),
    m_pIReq(NULL),
    m_pResponse(NULL),
    m_pRequest(NULL),
    m_pServer(NULL),
    m_punkScriptingNamespace(NULL),
    m_dwObjectContextCookie(NULL_GIP_COOKIE),
    m_pPageCompCol(NULL),
    m_pPageObjMgr(NULL),
    m_pActivity(NULL),
    m_ecsActivityScope(csUnknown),
    m_SessionId(INVALID_ID, 0, 0),
    m_pSession(NULL),
    m_pAppln(NULL),
    m_fRunGlobalAsa(FALSE),
    m_fStartSession(FALSE),
    m_fNewCookie(FALSE),
    m_fStartApplication(FALSE),
    m_fApplnOnStartFailed(FALSE),
    m_fClientCodeDebug(FALSE),
    m_fCompilationFailed(FALSE),
    m_fExecuting(FALSE),
    m_fHideRequestAndResponseIntrinsics(FALSE),
    m_fHideSessionIntrinsic(FALSE),
    m_fDoneWithSession(FALSE),
    m_fRejected(FALSE),
    m_f449Done(FALSE),
    m_fInTransferOnError(FALSE),
    m_pScriptingContext(NULL),
    m_nScriptTimeout(0),
    m_eExecStatus(eExecSucceeded),
    m_eEventState(eEventNone),
    m_uCodePage(CP_ACP),
    m_lcid(LOCALE_SYSTEM_DEFAULT),
    m_dwtTimestamp(0),
    m_pEngineInfo(NULL),
    m_pdispTypeLibWrapper(NULL),
    m_szCurrTemplateVirtPath(NULL),
    m_szCurrTemplatePhysPath(NULL),
    m_pASPError(NULL),
    m_pTemplate(NULL),
    m_fSecure(FALSE)
    {
    }

/*===================================================================
CHitObj::~CHitObj

Destructor

Parameters:
    None 

Returns:
    None
===================================================================*/
CHitObj::~CHitObj( void )
    {
    Assert(!m_fExecuting); // no deletes while still executing

    if (FIsBrowserRequest())
        {
        if (m_hImpersonate != hNil)
            m_hImpersonate = hNil;
            
        if (m_pSession)
            DetachBrowserRequestFromSession();
        }

    if (m_pASPError) // Error object
        {
        m_pASPError->Release();
        m_pASPError = NULL;
        }
        
    if (m_pActivity) // page-level Viper activity
        {
        delete m_pActivity;
        m_pActivity = NULL;
        }

    StopComponentProcessing();

    if (m_pdispTypeLibWrapper)
        m_pdispTypeLibWrapper->Release();

    // update request counters in application and session manager
    
    if (m_pAppln)
        {
        if (FIsBrowserRequest())
            {
            m_pAppln->DecrementRequestCount();
            }
        else if (FIsSessionCleanupRequest() && m_pAppln->PSessionMgr())
            {
            m_pAppln->PSessionMgr()->DecrementSessionCleanupRequestCount();
            }
        }

    if (m_pTemplate)
        m_pTemplate->Release();

    // update global request counters
    
    if (FIsBrowserRequest())
        InterlockedDecrement((LPLONG)&g_nBrowserRequests);
    else if (FIsSessionCleanupRequest())
        InterlockedDecrement((LPLONG)&g_nSessionCleanupRequests);
    else if (FIsApplnCleanupRequest())
        InterlockedDecrement((LPLONG)&g_nApplnCleanupRequests);

    if (m_pIReq)
        m_pIReq->Release();
    }

/*===================================================================
CHitObj::NewBrowserRequest

Static method. Creates, Inits, Posts new browser request

Parameters:
    pIReq           CIsapiReqInfo
    pfRejected      [out] TRUE if rejected  (optional)
    pfCompeleted    [out] TRUE if comleted  (optional)
    piErrorId       [out] error id          (optional)

Returns:
    S_OK on success
    E_FAIL  on failure
===================================================================*/
HRESULT CHitObj::NewBrowserRequest
(
CIsapiReqInfo *pIReq,
BOOL *pfRejected,
BOOL *pfCompleted,
int  *piErrorId
)
    {
    HRESULT hr = S_OK;
    BOOL fRejected = FALSE;
    BOOL fCompleted = FALSE;
    int  iError = 0;

    CHitObj *pHitObj = new CHitObj;
    if (!pHitObj)
        hr = E_OUTOFMEMORY;

    if (SUCCEEDED(hr))
        {
        // Bracket BrowserRequestInit

        if (SUCCEEDED(StartISAThreadBracket(pIReq)))
            {
            hr = pHitObj->BrowserRequestInit(pIReq, &iError);

            if (SUCCEEDED(hr))
                {
                if (pHitObj->FDoneWithSession())
                    {
                    // finished while on I/O thread
                    fCompleted = TRUE;
                    delete pHitObj;
                    pHitObj = NULL;
                    }
                }
            else // if FAILED
                {
                if (iError == IDE_SERVER_TOO_BUSY)
                    fRejected = TRUE;
                }
            
            EndISAThreadBracket(pIReq);
            }
        else
            {
            hr = E_FAIL;
            }
        }

    // Post into Viper
    if (SUCCEEDED(hr) && !fCompleted)
        {
        hr = pHitObj->PostViperAsyncCall();

        if (FAILED(hr))
            fRejected = TRUE;
        }

    if (FAILED(hr) && pHitObj)
        {
        // Bracket HitObj destructor
        
        // This code is only executed if PostViperAsyncCall either
        // failed or was never called -- thus we don't worry about
        // everlapping bracketing with the worker thread.
        
        if (SUCCEEDED(StartISAThreadBracket(pIReq)))
            {
            pHitObj->m_fRejected = TRUE;
            delete pHitObj;
            
            EndISAThreadBracket(pIReq);
            }
        }

    if (pfRejected)
        *pfRejected = fRejected;
    if (pfCompleted)
        *pfCompleted = fCompleted;
    if (piErrorId)
        *piErrorId = iError;
    
    return hr;
}

/*===================================================================
HRESULT CHitObj::SetCodePage

Set Runtime CodePage, if fAllowSessionState is On, this will set 
Session.CodePage and we should always use Session.CodePage when we
call HitObj.GetCodePage when fAllowSessionState on.

HitObj.CodePage is only set when fAllowSessionState is off or 
ApplicationCleanup, because we don't have Session.CodePage anymore, session
does not even exist.

Parameters:
    UINT    uCodePage

Returns:
    S_OK on success
    E_FAIL  on failure
===================================================================*/
HRESULT CHitObj::SetCodePage(UINT uCodePage)
{
    HRESULT hr = S_OK;
    
    if (uCodePage == CP_ACP || IsValidCodePage(uCodePage))
        {
        m_uCodePage = uCodePage;

        // If engine info is available, notify the scripts engines that the code
        // page has changed
        if (m_pEngineInfo)
            {
            for (int i = 0; i < m_pEngineInfo->cActiveEngines; i++)
                {
                Assert(m_pEngineInfo->rgActiveEngines[i].pScriptEngine != NULL);
                m_pEngineInfo->rgActiveEngines[i].pScriptEngine->UpdateLocaleInfo(hostinfoCodePage); 
                }
            }

        return hr;
        }

    return E_FAIL;
}

/*===================================================================
HRESULT CHitObj::SetLCID

Set Runtime LCID, if fAllowSessionState is On, this will set 
Session.LCID and we should always use Session.LCID when we
call HitObj.LCID when fAllowSessionState on.

HitObj.LCID is only set when fAllowSessionState is off or 
ApplicationCleanup, because we don't have Session.CodePage anymore, session
does not even exist.

Parameters:
    LCID    lcid

Returns:
    S_OK on success
    E_FAIL  on failure
===================================================================*/
HRESULT CHitObj::SetLCID(LCID lcid)
{
    HRESULT hr = S_OK;

    if ((LOCALE_SYSTEM_DEFAULT == lcid) || IsValidLocale(lcid, LCID_INSTALLED))
        {
        m_lcid = lcid;

        // If engine info is available, notify the scripts engines that the
        // lcid has changed
        if (m_pEngineInfo)
            {
            for (int i = 0; i < m_pEngineInfo->cActiveEngines; i++)
                {
                Assert(m_pEngineInfo->rgActiveEngines[i].pScriptEngine != NULL);
                m_pEngineInfo->rgActiveEngines[i].pScriptEngine->UpdateLocaleInfo(hostinfoLocale); 
                }
            }

        return hr;
        }

    return E_FAIL;
}

/*===================================================================
HRESULT CHitObj::BrowserRequestInit

Initialize the request object

Parameters:
    CIsapiReqInfo   *pIReq
    int *pErrorId

Returns:
    S_OK on success
    E_FAIL  on failure
===================================================================*/
HRESULT CHitObj::BrowserRequestInit
(
CIsapiReqInfo   *pIReq,
int  *pErrorId 
)
    {
    HRESULT hr;

    m_pIReq = pIReq;

    m_pIReq->AddRef();

    m_ehtType = ehtBrowserRequest;
    InterlockedIncrement((LPLONG)&g_nBrowserRequests);

#ifdef SCRIPT_STATS
    InterlockedIncrement(&g_cRequests);
#endif // SCRIPT_STATS

    STACK_BUFFER( serverPortSecureBuff, 8 );
    DWORD cbServerPortSecure;
    if( !SERVER_GET (pIReq,"SERVER_PORT_SECURE", &serverPortSecureBuff, &cbServerPortSecure))
    {
        if (GetLastError() == E_OUTOFMEMORY) 
        {
            return E_OUTOFMEMORY;
        }
    }
    char *szServerPortSecure = (char *)serverPortSecureBuff.QueryPtr();
    m_fSecure = (szServerPortSecure[0] == '1' );

    // Ask W3SVC for the impersonation token so we can later impersonate on Viper's thread
    if (FIsWinNT())
        m_hImpersonate = m_pIReq->QueryImpersonationToken();

    // Uppercase path - BUGBUG - can't Normalize in place!!!!
    Normalize(m_pIReq->QueryPszPathTranslated());

    // Reject direct requests for global.asa file
    if (FIsGlobalAsa(m_pIReq->QueryPszPathTranslated(), m_pIReq->QueryCchPathTranslated()))
        {
        *pErrorId = IDE_GLOBAL_ASA_FORBIDDEN;
        return E_FAIL;
        }

    // Create page component collection
    hr = InitComponentProcessing();
    if (FAILED(hr))
        {
        *pErrorId = (hr == E_OUTOFMEMORY) ? IDE_OOM : IDE_INIT_PAGE_LEVEL_OBJ;
        return hr;
        }

    // Attach to application (or create a new one)
    BOOL fApplnRestarting = FALSE;
    hr = AssignApplnToBrowserRequest(&fApplnRestarting);
    if (FAILED(hr))
        {
		*pErrorId = fApplnRestarting ? IDE_GLOBAL_ASA_CHANGED
									 : IDE_ADD_APPLICATION;
        return E_FAIL;
        }

    // Get Session cookie, and misc flags from http header
    hr = ParseCookiesForSessionIdAndFlags();
    if (FAILED(hr)) // no cookie is not an error -- failed here means OOM
        return hr;

    // Remember script timeout value
    m_nScriptTimeout = m_pAppln->QueryAppConfig()->dwScriptTimeout();

    // Check if the session is needed
    BOOL fAllowSessions = m_pAppln->QueryAppConfig()->fAllowSessionState();
    BOOL fNeedSession = fAllowSessions;

    // Look if the template is cached
    CTemplate *pTemplate = NULL;

    // Find in cache - don't load if not in cache already
    hr = g_TemplateCache.FindCached
        (
        m_pIReq->QueryPszPathTranslated(),
        DWInstanceID(),
        &pTemplate
        );

    if (hr == S_OK)
        {
        Assert(pTemplate);
        
        // store the template away for later use...
        //pTemplate->AddRef();
        //m_pTemplate = pTemplate;

        if (fAllowSessions)
            {
            // check for session-less templates
            fNeedSession = pTemplate->FSession();
            }
        else
            {
#ifdef SCRIPT_STATS
            if (pTemplate->FScriptless())
                InterlockedIncrement(&g_cScriptlessRequests);
#endif // SCRIPT_STATS

            // check for scipt-less templates to be
            // completed on the I/O thread (when no debugging)
            if (
#ifdef SCRIPT_STATS
                g_fSendScriptlessOnAtqThread &&
#endif // SCRIPT_STATS
                pTemplate->FScriptless() && !m_pAppln->FDebuggable())
                {
#ifdef SCRIPT_STATS
                LONG c = InterlockedIncrement(&g_cConcurrentScriptlessRequests);
                DWORD dwTime = GetTickCount();
#endif // SCRIPT_STATS

                if (SUCCEEDED(CResponse::SyncWriteScriptlessTemplate(m_pIReq, pTemplate)))
                    {
#ifndef PERF_DISABLE
                    g_PerfData.Incr_REQPERSEC();
                    g_PerfData.Incr_REQSUCCEEDED();
#endif
                    m_fDoneWithSession = TRUE;  // do not post to MTS
                    }

#ifdef SCRIPT_STATS
                dwTime = GetTickCount() - dwTime;
                InterlockedDecrement(&g_cConcurrentScriptlessRequests);
                
                g_lockRequestStats.WriteLock();
                    g_nSumExecTimeScriptlessRequests += dwTime;

                    if (c > g_cMaxConcurrentScriptlessRequests)
                        g_cMaxConcurrentScriptlessRequests = c;
                    g_nSumConcurrentScriptlessRequests += c;

                    g_nAvgConcurrentScriptlessRequests = (LONG)
                        (g_nSumConcurrentScriptlessRequests
                         / g_cScriptlessRequests);
                    g_nAvgExecTimeScriptlessRequests = (LONG)
                        (g_nSumExecTimeScriptlessRequests
                         / g_cScriptlessRequests);
                g_lockRequestStats.WriteUnlock();
#endif // SCRIPT_STATS
                }
            }

        // When possible, generate 449 cookies while on I/O thread
        if (!m_fDoneWithSession)
            {
                if (!SUCCEEDED(pTemplate->Do449Processing(this)))
                    g_TemplateCache.Flush(m_pIReq->QueryPszPathTranslated(), DWInstanceID());
            }

        pTemplate->Release();
        }

    // initialize CodePage and LCID to the app defaults...

    m_uCodePage = PAppln()->QueryAppConfig()->uCodePage();

    m_lcid = PAppln()->QueryAppConfig()->uLCID();

    if (!fNeedSession || m_fDoneWithSession)
    {
        m_fInited = TRUE;
        return S_OK;
    }

    // Attach to session or create a new one
    BOOL fNewSession, fNewCookie;
    hr = AssignSessionToBrowserRequest(&fNewSession, &fNewCookie, pErrorId);

    if (FAILED(hr))
        return E_FAIL;

    Assert(m_pSession);
    
    // Move from inside "if (fNewSesson)"
    if (fNewCookie)
        m_fNewCookie = TRUE;

    if (fNewSession)
        {
        m_fStartSession = TRUE;

        if (m_pAppln->FHasGlobalAsa())
            m_fRunGlobalAsa = TRUE;
        }

    m_fInited = TRUE;
    return S_OK;
    }

/*===================================================================
CHitObj::AssignApplnToBrowserRequest

Find or create a new appln for this browser request
Does the appln manager locking

Parameters:
    pfApplnRestarting   [out] flag - failed because the appln
                                     found is restarting

Returns:
    HRESULT
===================================================================*/
HRESULT CHitObj::AssignApplnToBrowserRequest
(
BOOL *pfApplnRestarting
)
    {
    HRESULT hr;
    
    Assert(pfApplnRestarting);
    *pfApplnRestarting = FALSE;
    
    Assert(!m_pAppln);

    TCHAR *szAppMDPath = m_pIReq->QueryPszApplnMDPath();
    if (!szAppMDPath)
        return E_FAIL;
        
    // Lock the application manager
    g_ApplnMgr.Lock();

    // Find by application by metabase key
    CAppln *pAppln;
    hr = g_ApplnMgr.FindAppln(szAppMDPath, &pAppln);

    if (hr == S_OK)
        {
        // Reject requests for restarting applications
        if (pAppln->FGlobalChanged())
            {
            *pfApplnRestarting = TRUE;
            g_ApplnMgr.UnLock();
            return E_FAIL;
            }
        // Update appln config from metabase if needed
        else if (pAppln->FConfigNeedsUpdate())
            {
            // If debugging flag has changed, then restart the application
            BOOL fRestartAppln = FALSE;
            BOOL fFlushAll = FALSE;
            pAppln->UpdateConfig(m_pIReq, &fRestartAppln, &fFlushAll);

            if (fRestartAppln)
                {
                pAppln->Restart(TRUE);      // force a restart
                pAppln = NULL;

                if (fFlushAll)  // flush all can only happen when restart is TRUE
                    {
                    // do flush while unlocked
                    g_ApplnMgr.UnLock();
                    g_TemplateCache.FlushAll();
                    g_ApplnMgr.Lock();
                    }
                
                // Find again
                hr = g_ApplnMgr.FindAppln(szAppMDPath, &pAppln);

                // Reject if still restarting
                if (hr == S_OK && pAppln->FGlobalChanged())
                    {
                    *pfApplnRestarting = TRUE;
                    g_ApplnMgr.UnLock();
                    return E_FAIL;
                    }
                }
            else
                {
                // adjust sctipt killer timeout
                g_ScriptManager.AdjustScriptKillerTimeout
                    (
                    // application timeout / 2 (in ms)
                    pAppln->QueryAppConfig()->dwScriptTimeout() * 500
                    );
                }
            }
        }
        
    if (hr != S_OK) // Application NOT found
        {
        TCHAR *szAppPhysicalPath = GetSzAppPhysicalPath();
        if (!szAppPhysicalPath)
            {
            g_ApplnMgr.UnLock();
            return E_FAIL;
            }

        // try to create a new one
        hr = g_ApplnMgr.AddAppln
            (
            szAppMDPath, // metabase key 
            szAppPhysicalPath, 
            m_pIReq,
            m_hImpersonate,
            &pAppln
            );

        if (FAILED(hr))
            {
            g_ApplnMgr.UnLock();
            free (szAppPhysicalPath);
            return hr;
            }

        // Check for GLOBAL.ASA

        TCHAR szGlobalAsaPath[MAX_PATH*2];
        DWORD cchPath = _tcslen(szAppPhysicalPath);
        _tcscpy(szGlobalAsaPath, szAppPhysicalPath);

        // BUG FIX: 102010 DBCS code fixes
        //if (szGlobalAsaPath[cchPath-1] != '\\')
        if ( *CharPrev(szGlobalAsaPath, szGlobalAsaPath + cchPath) != _T('\\'))
            szGlobalAsaPath[cchPath++] = _T('\\');
            
        _tcscpy(szGlobalAsaPath+cchPath, SZ_GLOBAL_ASA);

        // Check if GLOBAL.ASA exists
        BOOL fGlobalAsaExists = FALSE;
        if (SUCCEEDED(AspGetFileAttributes(szGlobalAsaPath)))
            {
            fGlobalAsaExists = TRUE;
            }
        else if (GetLastError() == ERROR_ACCESS_DENIED)
            {
            // If the current user doesn't have access (could happen when
            // there's an ACL on directory) try under SYSTEM user
            
            if (m_hImpersonate)
                {
                RevertToSelf();
                if (SUCCEEDED(AspGetFileAttributes(szGlobalAsaPath)))
                    fGlobalAsaExists = TRUE;
                HANDLE hThread = GetCurrentThread();
                SetThreadToken(&hThread, m_hImpersonate);
                }
            }

        if (fGlobalAsaExists)
            pAppln->SetGlobalAsa(szGlobalAsaPath);

        // Start monitoring application directory to
        // catch changes to GLOBAL.ASA even if it's not there
        if (FIsWinNT())
            {
            g_FileAppMap.AddFileApplication(szGlobalAsaPath, pAppln);
            CASPDirMonitorEntry *pDME = NULL;
            if (RegisterASPDirMonitorEntry(szAppPhysicalPath, &pDME, TRUE))
                pAppln->AddDirMonitorEntry(pDME);
            }

        free(szAppPhysicalPath);
        szAppPhysicalPath = NULL;

        // Update config from registry - don't care about restart
        // application is fresh baked
        pAppln->UpdateConfig(m_pIReq);

        // Adjust script killer timeout to current application
        g_ScriptManager.AdjustScriptKillerTimeout
            (
            // application timeout / 2 (in ms)
            pAppln->QueryAppConfig()->dwScriptTimeout() * 500
            );
        }

    // We have an application at this point
    Assert(pAppln);
    m_pAppln = pAppln;

    // Increment request count before releasing ApplMgr lock
    // to make sure it will not remove the app from under us
    m_pAppln->IncrementRequestCount();

    // Unlock the application manager
    g_ApplnMgr.UnLock();

    return S_OK;
    }
    
/*===================================================================
CHitObj::AssignSessionToBrowserRequest

Find or create a new session for this browser request
Does the session manager locking

Parameters:
    pfNewSession        [out] flag - new session created
    pfNewCookie         [out] flag - new cookie crated
    pErrorId            [out] -- error ID if failed

Returns:
    HRESULT
===================================================================*/
HRESULT CHitObj::AssignSessionToBrowserRequest
(
BOOL *pfNewSession,
BOOL *pfNewCookie,
int  *pErrorId
)
    {
    Assert(pfNewSession);
    Assert(pfNewCookie);
    
    Assert(!m_pSession);

    // Local vars

    BOOL fTooManySessions = FALSE;
    BOOL fUseNewSession = FALSE;
    BOOL fUseOldSession = FALSE;
    BOOL fUseNewCookie = FALSE;
    
    CSession *pNewSession = NULL; // newly created
    CSession *pOldSession = NULL; // existing session that is found
    
    BOOL fReuseIdAndCookie = FALSE;
    BOOL fValidId = g_SessionIdGenerator.IsValidId(m_SessionId.m_dwId);

    HRESULT hr = S_OK;

    CSessionMgr *pSessionMgr = m_pAppln->PSessionMgr();

    while (1)
        {
        // Try to find session by Id

        if (fValidId)
            {
            pSessionMgr->LockMaster();
            
            pOldSession = NULL;
            HRESULT hrFind = pSessionMgr->FindInMasterHash
                (
                m_SessionId,
                &pOldSession
                );

            // Good old Session?
            if (hrFind == NOERROR) // QFE has this condition as hrFind == NOERROR  
                {
                Assert(pOldSession);

                // If AspKeepSessionIDSecure is set in metabase and
                // they are going from a nonsecure to a secure connection then 
                // transition the user from their old http sessionid to their
                // new https secure session id
                if (QueryAppConfig()->fKeepSessionIDSecure() &&
                    FSecure() &&
                    !pOldSession->FSecureSession()
                    )
                {
                    // Generate New Cookie
                    hr = pSessionMgr->GenerateIdAndCookie
                        (
                        &m_SessionId,
                        m_szSessionCookie
                        );
                            
                    if (SUCCEEDED(hr))
                    {
                        hr = pSessionMgr->ChangeSessionId(pOldSession,m_SessionId);
                    }            

                    if (FAILED(hr))
                    {
                        pSessionMgr->UnLockMaster();
                        break;
                    }

                    pOldSession->SetSecureSession(TRUE);
                    fUseNewCookie = TRUE;                    
                }
                
                // Increment request count before unlock to avoid 
                // deletion of the session by other threads
                pOldSession->IncrementRequestsCount();
                pSessionMgr->UnLockMaster();
                fUseOldSession = TRUE;
                break;
                }

            // Bad old Session?
            else if (pOldSession)
                {
                pSessionMgr->UnLockMaster();
                fValidId = FALSE;
                }

            // No old session and we have a new session to insert?
            else if (pNewSession)
                {
                hr = pSessionMgr->AddToMasterHash(pNewSession);
                    
                if (SUCCEEDED(hr))
                    {
                    // Increment request count before unlock to avoid 
                    // deletion of the session by other threads
                    pNewSession->IncrementRequestsCount();
                    fUseNewSession = TRUE;
                    }
                pSessionMgr->UnLockMaster();
                break;
                }

            // No old session and no new session
            else
                {
                pSessionMgr->UnLockMaster();

                if (FSecure () && QueryAppConfig()->fKeepSessionIDSecure())
                    {
                        fValidId = FALSE;
                    }                
                }
            }

        // Generate id and cookie when needed

        if (!fValidId)  // 2nd time generate new id
            {
            hr = pSessionMgr->GenerateIdAndCookie
                (
                &m_SessionId,
                m_szSessionCookie
                );
            if (FAILED(hr))
                break;
            fValidId = TRUE;
            fUseNewCookie = TRUE;
            }
        
        // Create new session object if needed

        if (!pNewSession)
            {
            // Enforce the session limit for the application
            DWORD dwSessionLimit = m_pAppln->QueryAppConfig()->dwSessionMax();
            if (dwSessionLimit != 0xffffffff && dwSessionLimit != 0 &&
                m_pAppln->GetNumSessions() >= dwSessionLimit)
                {
                fTooManySessions = TRUE;
                hr = E_FAIL;
                break;
                }

            hr = pSessionMgr->NewSession(m_SessionId, &pNewSession);
            if (FAILED(hr))
                break;
            }
        else
            {
            // Assign new id to already created new session
            pNewSession->AssignNewId(m_SessionId);
            }

        // continue with the loop
        }

    // the results

    if (fUseNewSession)
        {
        Assert(SUCCEEDED(hr));
        Assert(pNewSession);

        m_pSession = pNewSession;
        m_pSession->SetSecureSession(FSecure());
        pNewSession = NULL;  // not to be deleted later
        }
    else if (fUseOldSession)
        {
        Assert(SUCCEEDED(hr));
        Assert(pOldSession);
        
        m_pSession = pOldSession;
        }
    else
        {
        Assert(FAILED(hr));
        
        *pErrorId = fTooManySessions ? IDE_TOO_MANY_USERS : IDE_ADD_SESSION;
        }
        
    // cleanup new session if unused
    if (pNewSession)
        {
        pNewSession->UnInit();
        pNewSession->Release();
        pNewSession = NULL;
        }

    if (m_pSession && m_pSession->FCodePageSet()) {
        m_uCodePage = m_pSession->GetCodePage();
    }
    else {
        m_uCodePage = PAppln()->QueryAppConfig()->uCodePage();
    }

    if (m_pSession && m_pSession->FLCIDSet()) {
        m_lcid = m_pSession->GetLCID();
    }
    else {
        m_lcid = PAppln()->QueryAppConfig()->uLCID();
    }

    // return flags
    *pfNewSession = fUseNewSession;
    *pfNewCookie  = fUseNewCookie;
    return hr;
    }

/*===================================================================
CHitObj::DetachBrowserRequestFromSession

Removes session from browser request.
Does session clean-up when needed

Parameters:

Returns:
    HRESULT
===================================================================*/
HRESULT CHitObj::DetachBrowserRequestFromSession()
    {
    Assert(m_pSession);
    Assert(m_pSession->PAppln());

    if (IsShutDownInProgress() || m_pSession->FInTOBucket())
        {
        // nothing fancy on shutdown
        
        // or if the session is still in the timeout bucket
        // (could happen for rejected requests)
        
        m_pSession->DecrementRequestsCount();
        m_pSession = NULL;
        return S_OK;
        }
        
    CSessionMgr *pSessionMgr = m_pSession->PAppln()->PSessionMgr();
    Assert(pSessionMgr);

     // try to delete this session if this is the last pending request
    if (m_pSession->GetRequestsCount() == 1)
        {
        // convert to lightweight if possible
        m_pSession->MakeLightWeight();

        // check if need to delete now
        if (m_pSession->FShouldBeDeletedNow(TRUE))
            {
            pSessionMgr->LockMaster();

            // check if still need to delete now after locking
            if (m_pSession->FShouldBeDeletedNow(TRUE))
                {
                pSessionMgr->RemoveFromMasterHash(m_pSession);
                pSessionMgr->UnLockMaster();
                
                m_pSession->DecrementRequestsCount();
                pSessionMgr->DeleteSession(m_pSession, TRUE);
                m_pSession = NULL;
                return S_OK;
                }

            pSessionMgr->UnLockMaster();
            }
        }

    // We can end up here for a rejected requests only if there are
    // other (non-rejected) requests for this session.
    //
    // The category of rejected here does not include rejected because
    // of the RequestQueueMax. This only applies to real OOM situations.
    //
    // In case of rejected request or if there are other pending
    // requests for this, session these other requests will take
    // care of reinserting the session into the timeout bucket.
    //
    // Rejected requests are NOT serialized -- they don't run on Viper
    // threads. Inserting the session into a timeout bucket for a
    // rejected request might create a race condition with regular requests.
    
    if (!m_fRejected && m_pSession->GetRequestsCount() == 1)
        {
        // Insert into proper timeout bucket
        if (pSessionMgr->FIsSessionKillerScheduled())
            {
            pSessionMgr->UpdateSessionTimeoutTime(m_pSession);
            pSessionMgr->AddSessionToTOBucket(m_pSession);
            }
        }
    
    m_pSession->DecrementRequestsCount();

    // session is no longer attached to the request
    m_pSession = NULL;
    
    return S_OK;
    }

/*===================================================================
void CHitObj::SessionCleanupInit

Initialize a request object for session cleanup

Parameters:
    CSession *pSession      Session object context

Returns:
    NONE
===================================================================*/
void CHitObj::SessionCleanupInit
(
CSession * pSession
)
    {
    m_ehtType = ehtSessionCleanup;
    InterlockedIncrement((LPLONG)&g_nSessionCleanupRequests);

    HRESULT hr = InitComponentProcessing();
    if (FAILED(hr))
        {
        if (hr == E_OUTOFMEMORY)
            HandleOOMError(NULL, NULL);
        }

    m_pSession      = pSession;
    m_pAppln        = pSession->PAppln();
    m_fRunGlobalAsa = TRUE;
    m_pIReq          = NULL;

    if (m_pAppln->PSessionMgr())
        m_pAppln->PSessionMgr()->IncrementSessionCleanupRequestCount();

    m_fInited = TRUE;
    }

/*===================================================================
void CHitObj::ApplicationCleanupInit

Initializes a request object for application cleanup

Parameters:
    CAppln *        pAppln      Application object context

Returns:
    NONE
===================================================================*/
void CHitObj::ApplicationCleanupInit( CAppln * pAppln )
{
    m_ehtType = ehtApplicationCleanup;
    InterlockedIncrement((LPLONG)&g_nApplnCleanupRequests);

    // If OOM here, then cleanup request does not get a server object.
    HRESULT hr = InitComponentProcessing();
    if (FAILED(hr))
        {
        if (hr == E_OUTOFMEMORY)
            HandleOOMError(NULL, NULL);
        }
        
    m_pAppln = pAppln;
    m_fRunGlobalAsa = TRUE;
    m_pIReq = NULL;

    m_fInited = TRUE;
}

/*===================================================================
CHitObj::ReassignAbandonedSession

Reassign ID of a the session being abandonded thus
detaching it from the client

Parameters:

Returns:
    HRESULT
===================================================================*/
HRESULT CHitObj::ReassignAbandonedSession()
    {
    HRESULT hr = E_FAIL;
    
    Assert(m_pSession);
    Assert(m_pAppln);
    m_pAppln->AssertValid();

    hr = m_pAppln->PSessionMgr()->GenerateIdAndCookie
        (
        &m_SessionId,
        m_szSessionCookie
        );

    if (SUCCEEDED(hr))
        {
        hr = m_pAppln->PSessionMgr()->ChangeSessionId
            (
            m_pSession,
            m_SessionId
            );
        }
                            
    return hr;
    }

/*===================================================================
void CHitObj::FObjectTag

Check if the object passed in as argument is an object tag
created object.

Parameters:
    IDispatch * pDispatch   pointer to object

Returns:
    TRUE    Is a object tag created object
    FALSE   Otherwise
===================================================================*/
BOOL CHitObj::FObjectTag( IDispatch * pDispatch )
{
    if (!m_pPageObjMgr)
        return FALSE;
        
    BOOL fRet = TRUE;

    CComponentObject *pObj = NULL;
    HRESULT hr = m_pPageObjMgr->
        FindAnyScopeComponentByIDispatch(pDispatch, &pObj);

    return (SUCCEEDED(hr) && pObj);
}

/*  buffer allows space for: <user cookie>  +    CCH_SESSION_ID_COOKIE  +   =   +   <cookie>        +   '\0')
                                50          +       20                  +   1   +   SESSIONID_LEN   +   1
    NOTE we arbitrarily allow 50 bytes for <user cookie>
    NOTE if CCH_SESSION_ID_COOKIE changes, CCH_BUFCOOKIES_DEFAULT must be changed accordingly
*/
#define CCH_BUFCOOKIES_DEFAULT  72 + SESSIONID_LEN

/*===================================================================
CHitObj::ParseCookiesForSessionIdAndFlags

Extracts Cookie from CIsapiReqInfo.

Parameters:

Side Effects:
    Initializes m_SessionId, m_SessionIdR1, m_SessionIdR2 and
                m_szSessionCookie
    Sets m_fClientCodeDebug flag

Returns:
    S_OK        Extracted cookie value successfully
    S_FALSE     Success, but no cookie found
    other       error
===================================================================*/
HRESULT CHitObj::ParseCookiesForSessionIdAndFlags()
    {
    Assert(m_pAppln);
    CAppConfig *pAppConfig = m_pAppln->QueryAppConfig();
    
    // Are we interested in ANY cookies?

    if (!pAppConfig->fAllowSessionState() && 
        !pAppConfig->fAllowClientDebug())
        return S_OK;

    // If session cookie is needed init it

    if (pAppConfig->fAllowSessionState())
        {
        m_SessionId.m_dwId = INVALID_ID;
        m_szSessionCookie[0] = '\0';
        }

    // Get cookies from WAM_EXEC_INFO
    char *szBufCookies = m_pIReq->QueryPszCookie();
    if (!szBufCookies || !*szBufCookies)
        return S_OK; // no cookies

    // Obtain Session Cookie (and ID) if needed
        
    if (pAppConfig->fAllowSessionState())
        {
        char *pT;

        if (pT = strstr(szBufCookies, g_szSessionIDCookieName))
            {
            pT += CCH_SESSION_ID_COOKIE;
            if (*pT == '=')
                {
                pT++;
                if (strlen( pT ) >= SESSIONID_LEN)
                    {
                    memcpy(m_szSessionCookie, pT, SESSIONID_LEN);
                    m_szSessionCookie[SESSIONID_LEN] = '\0';
                    }
                }
            }

        // validate and try to decode the session id cookie
        if (m_szSessionCookie[0] != '\0')
            {
            if (FAILED(DecodeSessionIdCookie
                    (
                    m_szSessionCookie,
                    &m_SessionId.m_dwId, 
                    &m_SessionId.m_dwR1,
                    &m_SessionId.m_dwR2
                    )))
                {
                m_SessionId.m_dwId = INVALID_ID;
                m_szSessionCookie[0] = '\0';
                }
            }
        }

    // Look for Client Debug enabling cookie

    if (pAppConfig->fAllowClientDebug())
        {
        if (strstr(szBufCookies, SZ_CLIENT_DEBUG_COOKIE"="))
            m_fClientCodeDebug = TRUE;
        }

    return S_OK;
    }

/*===================================================================
BOOL CHitObj::GetSzAppPhysicalPath

Extracts application directory from WAM_EXEC_INFO

Parameters:

Side Effects:
On success, allocate memory for pszAppPhysicalPath

Returns:
    TRUE        AppPhysicalPath
    FALSE       NULL
===================================================================*/
TCHAR *CHitObj::GetSzAppPhysicalPath()
{
    DWORD  dwSizeofBuffer = 265*sizeof(TCHAR);
    TCHAR  *pszAppPhysicalPathLocal = (TCHAR *)malloc(dwSizeofBuffer);
    CHAR   *pszApplPhysPathVarName;

    if (!pszAppPhysicalPathLocal)
        return NULL;

#if UNICODE
    pszApplPhysPathVarName = "UNICODE_APPL_PHYSICAL_PATH";
#else
    pszApplPhysPathVarName = "APPL_PHYSICAL_PATH";
#endif

    BOOL fFound = m_pIReq->GetServerVariable
        (
        pszApplPhysPathVarName, 
        pszAppPhysicalPathLocal, 
        &dwSizeofBuffer
        );

    if (!fFound)
        {
        DWORD dwErr = GetLastError();

        if (ERROR_INSUFFICIENT_BUFFER == dwErr)
            {
            // Not Enough Buffer
            free(pszAppPhysicalPathLocal);
            pszAppPhysicalPathLocal = (TCHAR *)malloc(dwSizeofBuffer);
            if (pszAppPhysicalPathLocal)
                {
                // Try again
                fFound = m_pIReq->GetServerVariable
                    (
                    pszApplPhysPathVarName, 
                    pszAppPhysicalPathLocal, 
                    &dwSizeofBuffer
                    );
                }
            }
        }

    if (!fFound) {
        if (pszAppPhysicalPathLocal) {
            free(pszAppPhysicalPathLocal);
            pszAppPhysicalPathLocal = NULL;
        }
    }
    else
        {
        Assert(pszAppPhysicalPathLocal);
        Normalize(pszAppPhysicalPathLocal);
        }

    return pszAppPhysicalPathLocal;
    }

/*===================================================================
CHitObj::InitComponentProcessing

Creates and inits component collection and page object manager

Parameters:

Returns:
    HRESULT
===================================================================*/
HRESULT CHitObj::InitComponentProcessing()
    {
    Assert(!m_pPageCompCol);
    Assert(!m_pPageObjMgr);

    HRESULT hr = S_OK;

    // Page component collection

    m_pPageCompCol = new CComponentCollection;
    if (!m_pPageCompCol)
        return E_OUTOFMEMORY;

    hr = m_pPageCompCol->Init(csPage);
    if (FAILED(hr))
        return hr;

    // Page object manager
        
    m_pPageObjMgr = new CPageComponentManager;
    if (!m_pPageObjMgr)
        return E_OUTOFMEMORY;

    hr = m_pPageObjMgr->Init(this);
    if (FAILED(hr))
        return hr;

    return S_OK;
    }

/*===================================================================
CHitObj::StopComponentProcessing

Deletes component collection and page object manager

Parameters:

Returns:
    HRESULT
===================================================================*/
HRESULT CHitObj::StopComponentProcessing()
    {
    if (m_pPageObjMgr)
        {
        delete m_pPageObjMgr;
        m_pPageObjMgr = NULL;
        }

    if (m_pPageCompCol)
        {
        delete m_pPageCompCol;
        m_pPageCompCol = NULL;
        }

    if (m_punkScriptingNamespace)
        {
        m_punkScriptingNamespace->Release();
        m_punkScriptingNamespace = NULL;
        }
        
    if (m_dwObjectContextCookie != NULL_GIP_COOKIE)
        {
        RemoveObjectContext();
        }

    return S_OK;
    }
    
/*===================================================================
CHitObj::GetPageComponentCollection

Returns component collection for page

Parameters:
    CComponentCollection **ppCollection     output

Returns:
    HRESULT
===================================================================*/
HRESULT CHitObj::GetPageComponentCollection
(
CComponentCollection **ppCollection
)
    {
    *ppCollection = m_pPageCompCol;
    return (*ppCollection) ? S_OK : TYPE_E_ELEMENTNOTFOUND;
    }

/*===================================================================
CHitObj::GetSessionComponentCollection

Returns component collection for session

Parameters:
    CComponentCollection **ppCollection     output

Returns:
    HRESULT
===================================================================*/
HRESULT CHitObj::GetSessionComponentCollection
(
CComponentCollection **ppCollection
)
    {
    if (m_pSession)
        {
        *ppCollection = m_pSession->PCompCol();

        if (*ppCollection == NULL             &&  // no collection
            m_eEventState != eEventAppOnStart &&  // not an application
            m_eEventState != eEventAppOnEnd)      //       level event
            {
            // init session collection on demand
            HRESULT hr = m_pSession->CreateComponentCollection();
            if (SUCCEEDED(hr))
                *ppCollection = m_pSession->PCompCol();
            }
        }
    else
        *ppCollection = NULL;
        
    return (*ppCollection) ? S_OK : TYPE_E_ELEMENTNOTFOUND;
    }

/*===================================================================
CHitObj::GetApplnComponentCollection

Returns component collection for application

Parameters:
    CComponentCollection **ppCollection     output

Returns:
    HRESULT
===================================================================*/
HRESULT CHitObj::GetApplnComponentCollection
(
CComponentCollection **ppCollection
)
    {
    if (m_pAppln)
        *ppCollection = m_pAppln->PCompCol();
    else
        *ppCollection = NULL;
        
    return (*ppCollection) ? S_OK : TYPE_E_ELEMENTNOTFOUND;
    }

/*===================================================================
CHitObj::AddComponent

Adds uninstantiated tagged object to appropriate
component collection

Parameters:
    CompType  type
    const CLSID &clsid
    CompScope scope
    CompModel model
    LPWSTR     pwszName
    IUnknown  *pUnk

Returns:
    HRESULT
===================================================================*/
HRESULT CHitObj::AddComponent
(
CompType  type,
const CLSID &clsid,
CompScope scope,
CompModel model,
LPWSTR    pwszName,
IUnknown *pUnk
)
    {
    Assert(m_pPageObjMgr);
    m_pPageObjMgr->AssertValid();
 
    Assert(type == ctTagged);
    
    HRESULT hr = m_pPageObjMgr->AddScopedTagged
        (
        scope, 
        pwszName,
        clsid,
        model
        );

    return hr;
    }

/*===================================================================
CHitObj::GetComponent

Finds CComponentObject by scope and name

Parameters:
    CompScope         scope        can be csUnknown
    LPWSTR            pwszName     name to find
    DWORD             cbName       name length (in bytes)
    CComponentObject **ppObj       (out) object found

Returns:
    HRESULT     S_OK on success
                TYPE_E_ELEMENTNOTFOUND if the object wasnt found
                Other HRESULT if object fails to instantiate
===================================================================*/
HRESULT CHitObj::GetComponent
(
CompScope          scope, 
LPWSTR             pwszName, 
DWORD              cbName,
CComponentObject **ppObj
)
    {
    Assert(ppObj);
    *ppObj = NULL;

    if (!m_pPageObjMgr)
        return TYPE_E_ELEMENTNOTFOUND;
    
    BOOL fNewInstance = FALSE;
    HRESULT hr = m_pPageObjMgr->GetScopedObjectInstantiated
        (
        scope,
        pwszName,
        cbName,
        ppObj,
        &fNewInstance
        );

	if (FAILED(hr))
		return hr;

    // If an object that restricts threading has been instantiateed
    // as the session's tagged <OBJECT>, and the session's activity
    // runs this request, then bind the session's activity to thread

    if ((*ppObj)->GetScope() == csSession  && // session scope component
        m_ecsActivityScope == csSession    && // session scope activity
        SUCCEEDED(hr)                      && // get object succeeded
        fNewInstance                       && // object was just instantiated
        *ppObj && !(*ppObj)->FAgile())      // the object is thread-locked
        {
        m_pSession->PActivity()->BindToThread();
        }

    return hr;
    }

/*===================================================================
CHitObj::GetIntrinsic

Finds Intrinsic by name

Parameters:
    LPWSTR            pwszName     name to find
    DWORD             cbName       name length (in bytes)
    IUnknown        **ppUnk        (out) object found

Returns:
    HRESULT     S_OK on success
                S_FALSE name of the instrinsic but it's missing
                TYPE_E_ELEMENTNOTFOUND if the object not found
===================================================================*/
HRESULT CHitObj::GetIntrinsic
(
LPWSTR     pwszName, 
DWORD      cbName,
IUnknown **ppUnk
)
    {
    Assert(ppUnk);
    *ppUnk = NULL;


    // Lookup table based on (wszName[0] - cbName) % 32
    // Works for both uppper and lower case names

    static enum IntrinsicType
        {
        itUnknown = 0,
        itObjContext,
        itNamespace,
        itAppln,
        itSession,
        itRequest,
        itResponse,
        itServer,
        itASPPageTLB,
        itASPGlobalTLB
        }
    rgitLookupEntries[] =
        {
        /* 0-1   */     itUnknown, itUnknown,
        /* 2     */ itResponse,
        /* 3     */     itUnknown,
        /* 4     */ itRequest,
        /* 5     */ itSession,
        /* 6     */     itUnknown,
        /* 7     */ itServer,
        /* 8     */     itUnknown,
        /* 9     */ itASPGlobalTLB,
        /* 10    */     itUnknown,
        /* 11    */ itAppln,
        /* 12    */     itUnknown,
        /* 13    */ itASPPageTLB,
        /* 14    */     itUnknown,
        /* 15    */ itNamespace,
        /* 16-20 */     itUnknown, itUnknown, itUnknown, itUnknown, itUnknown,
        /* 21    */ itObjContext,
        /* 22-31 */     itUnknown, itUnknown, itUnknown, itUnknown, itUnknown,
                        itUnknown, itUnknown, itUnknown, itUnknown, itUnknown
        };

    IntrinsicType itType = rgitLookupEntries
        [
        (pwszName[0] - cbName) & 0x1f   // &1f same as %32
        ];

    if (itType == itUnknown)  // most likely
        return TYPE_E_ELEMENTNOTFOUND;

    // Do the string comparison
    BOOL fNameMatch = FALSE;
    
    switch (itType)
        {
    case itNamespace:
        if (_wcsicmp(pwszName, WSZ_OBJ_SCRIPTINGNAMESPACE) == 0)
            {
            fNameMatch = TRUE;
            *ppUnk = m_punkScriptingNamespace;
            }
        break;

    case itResponse:
        if (_wcsicmp(pwszName, WSZ_OBJ_RESPONSE) == 0)
            {
            fNameMatch = TRUE;
            if (!m_fHideRequestAndResponseIntrinsics)
                *ppUnk = static_cast<IResponse *>(m_pResponse);
            }
        break;

    case itRequest:
        if (_wcsicmp(pwszName, WSZ_OBJ_REQUEST) == 0)
            {
            fNameMatch = TRUE;
            if (!m_fHideRequestAndResponseIntrinsics)
                *ppUnk = static_cast<IRequest *>(m_pRequest);
            }
        break;
        
    case itSession:
        if (_wcsicmp(pwszName, WSZ_OBJ_SESSION) == 0)
            {
            fNameMatch = TRUE;
            if (!m_fHideSessionIntrinsic)
                *ppUnk = static_cast<ISessionObject *>(m_pSession);
            }
        break;
        
    case itServer:
        if (_wcsicmp(pwszName, WSZ_OBJ_SERVER) == 0)
            {
            fNameMatch = TRUE;
            *ppUnk = static_cast<IServer *>(m_pServer);
            }
        break;
        
    case itAppln:
        if (_wcsicmp(pwszName, WSZ_OBJ_APPLICATION) == 0)
            {
            fNameMatch = TRUE;
            *ppUnk = static_cast<IApplicationObject *>(m_pAppln);
            }
        break;
        
    case itObjContext:
        if (_wcsicmp(pwszName, WSZ_OBJ_OBJECTCONTEXT) == 0)
            {
            IASPObjectContext* pASPObjectContext = NULL;

            // WARNING. We're making two assumptions here. First, we
            //   assume that the GIT holds a reference to the object.
            //   Second, we assume that this script runs in the context
            //   that owns the object, so, the release won't destroy a
            //   proxy or anything. The problem is, the code we're
            //   replacing counted on the reference passed back to the
            //   script was also held here. There's obviously nobody
            //   "up there" that's releasing it, so, we have to do it
            //   here to simulate the old code.
            UseObjectContext(&pASPObjectContext);
			if (pASPObjectContext == NULL)
				{
				Assert (FALSE);
				return E_FAIL;		// should never happen...
				}

            *ppUnk = pASPObjectContext;
            pASPObjectContext->Release();
            fNameMatch = TRUE;
            }
            break;

    case itASPPageTLB:
        if (_wcsicmp(pwszName, WSZ_OBJ_ASPPAGETLB) == 0)
            {
            fNameMatch = TRUE;
            *ppUnk = m_pdispTypeLibWrapper;
            }
        break;

    case itASPGlobalTLB:
        if (_wcsicmp(pwszName, WSZ_OBJ_ASPGLOBALTLB) == 0)
            {
            fNameMatch = TRUE;
            *ppUnk = m_pAppln->PGlobTypeLibWrapper();
            }
        break;
        }

    if      (*ppUnk)        return S_OK;
    else if (fNameMatch)    return S_FALSE;
    else                    return TYPE_E_ELEMENTNOTFOUND;
    }

/*===================================================================
CHitObj::CreateComponent

Server.CreateObject calls this

Parameters:
    clsid       create of this CLSID
    ppDisp      return IDispatch*

Returns:
    HRESULT
===================================================================*/
HRESULT CHitObj::CreateComponent
(
const CLSID &clsid,
IDispatch **ppDisp
)
    {
    Assert(m_pPageObjMgr);

    CComponentObject *pObj = NULL;
    
    HRESULT hr = m_pPageObjMgr->AddScopedUnnamedInstantiated
        (
        csPage, 
        clsid, 
        cmUnknown,
        NULL,
        &pObj
        );
    if (FAILED(hr))
        {
        *ppDisp = NULL;
        return hr;
        }

    Assert(pObj);

    hr = pObj->GetAddRefdIDispatch(ppDisp);

    if (SUCCEEDED(hr))
        {
        // don't keep the object around unless needed
        if (pObj->FEarlyReleaseAllowed())
            m_pPageObjMgr->RemoveComponent(pObj);
        }

    return hr;
    }

/*===================================================================
CHitObj::SetPropertyComponent

Sets property value to variant

Parameters:
    CompScope         scope        property scope
    LPWSTR             pwszName     property name
    VARIANT            pVariant     property value to set

Returns:
    HRESULT
===================================================================*/
HRESULT CHitObj::SetPropertyComponent
(
CompScope scope,
LPWSTR     pwszName, 
VARIANT   *pVariant
)
    {
    if (!m_pPageObjMgr)
        return TYPE_E_ELEMENTNOTFOUND;

    CComponentObject *pObj = NULL;
    HRESULT hr = m_pPageObjMgr->AddScopedProperty(scope, pwszName, 
                                                  pVariant, &pObj);

    // If an object that restricts threading has been assigned as
    // the session property, and the session's activity runs this
    // request, then bind the session's activity to thread

    if (scope == csSession               && // session scope property
        m_ecsActivityScope == csSession  && // session scope activity
        SUCCEEDED(hr)                    && // set property succeed
        pObj && !pObj->FAgile())            // the object is thread-locked
        {
        m_pSession->PActivity()->BindToThread();
        }
        
    return hr;
    }

/*===================================================================
CHitObj::GetPropertyComponent

Finds property CComponentObject by scope and name

Parameters:
    CompScope         scope        wher to find
    LPWSTR             pwszName     name to find
    CComponentObject **ppObj        (out) object found

Returns:
    HRESULT     S_OK on success
                TYPE_E_ELEMENTNOTFOUND if the object wasnt found
                Other HRESULT
===================================================================*/
HRESULT CHitObj::GetPropertyComponent
(
CompScope         scope, 
LPWSTR             pwszName, 
CComponentObject **ppObj
)
    {
    *ppObj = NULL;
    
    if (!m_pPageObjMgr)
        return TYPE_E_ELEMENTNOTFOUND;

    return m_pPageObjMgr->GetScopedProperty(scope, pwszName, ppObj);
    }

/*===================================================================
CHitObj::SetActivity

Remember activity with CHitObj

Parameters
    CViperActivity *pActivity       Viper activity to remember
                                    (and later delete)

Returns:
    HRESULT
===================================================================*/
HRESULT CHitObj::SetActivity
(
CViperActivity *pActivity
)
    {
    Assert(!m_pActivity);
    m_pActivity = pActivity;
    return S_OK;
    }

/*===================================================================
CHitObj::PCurrentActivity

Returns Viper Activity, the current HitObj is running under

Parameters

Returns:
    CViperActivity *
===================================================================*/
CViperActivity *CHitObj::PCurrentActivity()
    {
    CViperActivity *pActivity = NULL;

    switch (m_ecsActivityScope)
        {
        case csPage:
            pActivity = m_pActivity;
            break;
        case csSession:
            Assert(m_pSession);
            pActivity = m_pSession->PActivity();
            break;
        case csAppln:
            Assert(m_pAppln);
            pActivity = m_pAppln->PActivity();
            break;
        }

    return pActivity;
    }

/*===================================================================
CHitObj::PostViperAsyncCall

Asks Viper to calls us back from the right thread to execute
the request.

Used instead of queueing

Returns:
    HRESULT

Side effects:
===================================================================*/
HRESULT CHitObj::PostViperAsyncCall()
    {
#ifndef PERF_DISABLE
    BOOL fDecrOnFail = FALSE;
    if (FIsBrowserRequest())
        {
        DWORD dwRequestQueued = g_PerfData.Incr_REQCURRENT();
#if 0 && defined(SCRIPT_STATS)
        if (g_dwQueueDebugThreshold != 0
            &&  dwRequestQueued >= g_dwQueueDebugThreshold)
            DebugBreak();
#endif
        fDecrOnFail = TRUE;
        }
#endif

    UpdateTimestamp();  // before posting into queue

    CViperActivity *pApplnAct = m_pAppln ?
        m_pAppln->PActivity() : NULL;
        
    CViperActivity *pSessnAct = m_pSession ?
        m_pSession->PActivity() : NULL;

    HRESULT hr;

    if (pApplnAct)
        {
        m_ecsActivityScope = csAppln;
        hr = pApplnAct->PostAsyncRequest(this);
        }
    else if (pSessnAct)
        {
        m_ecsActivityScope = csSession;
        hr = pSessnAct->PostAsyncRequest(this);
        }
    else
        {
        m_ecsActivityScope = csPage;
        hr = CViperActivity::PostGlobalAsyncRequest(this);
        }
        
#ifndef PERF_DISABLE
    if (FAILED(hr) && fDecrOnFail)
        g_PerfData.Decr_REQCURRENT();
#endif    

    return hr;
    }

/*===================================================================
CHitObj::ViperAsyncCallback

Viper calls us back from the right thread to execute
the request.

Used instead of queueing

Parameters
    BOOL       *pfRePosted   [out] flag TRUE if request re-posted
                             under diff activity (don't delete it)
  
Returns:
    HRESULT

Side effects:
===================================================================*/
HRESULT CHitObj::ViperAsyncCallback
(
BOOL *pfRePosted
)
    {
    HRESULT hr = S_OK;
    BOOL fTemplateInCache;
    
    *pfRePosted = FALSE;

    Assert(!m_fExecuting); // no nested executions of the same request
    m_fExecuting = TRUE;
    
    Assert(FIsValidRequestType());

    DWORD dwtWaitTime = ElapsedTimeSinceTimestamp();
    UpdateTimestamp();  // received from the queue

#ifndef PERF_DISABLE
    if (FIsBrowserRequest())
        {
        g_PerfData.Decr_REQCURRENT();
        g_PerfData.Set_REQWAITTIME(dwtWaitTime);
        }
#endif

    ///////////////////
    // Reject browser requests in certain situations
    
    if (FIsBrowserRequest())
        {
        BOOL fRejected = FALSE;
        RejectBrowserRequestWhenNeeded(dwtWaitTime, &fRejected);
        if (fRejected)
            return S_OK;
        }

    ///////////////////
    // Pass through the thread gate
    
    EnterThreadGate(m_dwtTimestamp);

    ///////////////////
    // Reject browser requests in certain situations
    
    if (FIsBrowserRequest() && IsShutDownInProgress())
        {
        BOOL fRejected = FALSE;
        RejectBrowserRequestWhenNeeded(dwtWaitTime, &fRejected);
        if (fRejected)
            return S_OK;
        }

    ///////////////////
    // Remove the session from it's timeout bucket 
    // while executing the request

    if (m_pSession && m_pSession->FInTOBucket())
        m_pAppln->PSessionMgr()->RemoveSessionFromTOBucket(m_pSession);

    ///////////////////
    // If there's an application level activity we need to make
    // sure this activity is bound to a thread. Could not bind it
    // before because it has to be Viper thread to bind to.

    CViperActivity *pApplnActivity = m_pAppln->PActivity();

    if (pApplnActivity && !pApplnActivity->FThreadBound())
        pApplnActivity->BindToThread();

    ///////////////////
    // Take care of first application request with GLOBAL.ASA
    // Lock application if needed

    BOOL fApplnLocked = FALSE;
    BOOL fFirstAppRequest = FALSE;

    if (FIsBrowserRequest() && m_pAppln->FHasGlobalAsa() &&
                              !m_pAppln->FFirstRequestRun())
        {
        m_pAppln->InternalLock();
        fApplnLocked = TRUE;

        if (!m_pAppln->FFirstRequestRun())
            {
            m_fStartApplication = TRUE;
            m_fRunGlobalAsa = TRUE;
            fFirstAppRequest = TRUE;
            }
        else
            {
            m_pAppln->InternalUnLock();
            fApplnLocked = FALSE;
            }
        }

    ///////////////////
    // Repost under a different activity if needed
    // (do it only after the first app request finished)

    if (!fApplnLocked) // if not processing first app request
        {
        CViperActivity *pSessnAct, *pApplnAct;
        CViperActivity *pRepostToActivity = NULL;
            
        switch (m_ecsActivityScope)
            {
            case csPage:
                // repost to session activity if any
                pSessnAct = m_pSession ? m_pSession->PActivity() : NULL;
                if (pSessnAct)
                    pRepostToActivity = pSessnAct;
                    
                // no break;
            case csSession:
                // repost to application activity if any
                pApplnAct = m_pAppln ? m_pAppln->PActivity() : NULL;
                if (pApplnAct)
                    pRepostToActivity = pApplnAct;
                    
                // no break;
            case csAppln:
                // never repost application activity request
                break;
            }

        if (pRepostToActivity)
            {
            LeaveThreadGate();   // notify the thead gate
            m_fExecuting = FALSE;  // before reposting to avoid nesting
            hr = pRepostToActivity->PostAsyncRequest(this);
            *pfRePosted = SUCCEEDED(hr);
            return hr;
            }
        }

    ///////////////////
    // Cleanup any scripting engines that need to be shut
    // down on this thread, if we are on a thread enabled
    // for debugging
 
    if (m_pAppln->FDebuggable() && FIsBrowserRequest())
        {
        Assert(m_ecsActivityScope == csAppln);
        g_ApplnMgr.CleanupEngines();
        if (!g_dwDebugThreadId)
            g_dwDebugThreadId = GetCurrentThreadId();
        }

    ///////////////////
    // Prepare intrinsics

    CIntrinsicObjects intrinsics;

    m_pServer = NULL;
    m_pResponse = NULL;
    m_pRequest = NULL;
    m_fHideRequestAndResponseIntrinsics = FALSE;
    m_fHideSessionIntrinsic = FALSE;
    m_punkScriptingNamespace = NULL;
    m_dwObjectContextCookie = NULL_GIP_COOKIE;

    hr = intrinsics.Prepare(m_pSession);

    if (FAILED(hr))  // couldn't setup intrinsics
        {
        if (fApplnLocked)
            m_pAppln->InternalUnLock();
            
#ifndef PERF_DISABLE
        g_PerfData.Incr_REQFAILED();
        g_PerfData.Incr_REQERRORPERSEC();
#endif            
        LeaveThreadGate();   // notify the thead gate
        m_fExecuting = FALSE;
        
        if (FIsBrowserRequest())
            ReportServerError(IDE_SERVER_TOO_BUSY);
            
        return hr;
        }

    if (FIsBrowserRequest())
        {
        m_pResponse = intrinsics.PResponse();
        m_pRequest  = intrinsics.PRequest();
        }
        
    m_pServer = intrinsics.PServer();
    
    Assert(!FIsBrowserRequest() || m_pResponse);

    ///////////////////
    // Point session to this hit object

    if (m_pSession)
        m_pSession->SetHitObj(this);
        
    ///////////////////
    // Impersonate
    
    HANDLE hThread = GetCurrentThread();

    if (FIsBrowserRequest())
        {
        if (FIsWinNT())
            {
            if (!SetThreadToken(&hThread, m_hImpersonate))
                {
#ifdef DBG
                // for debug purposes, it is interesting to know what the error was
                DWORD err = GetLastError();
#endif

                ReportServerError(IDE_IMPERSONATE_USER);
                m_eExecStatus = eExecFailed;
                hr = E_FAIL;
                }
            }
        }

    ///////////////////
    // Make Scripting Context

    if (SUCCEEDED(hr))
        {
        Assert(!m_pScriptingContext);
        
        m_pScriptingContext = new CScriptingContext
            (
            m_pAppln,
            m_pSession,
            m_pRequest,
            m_pResponse,
            m_pServer
            );
            
        if (!m_pScriptingContext)
            hr = E_OUTOFMEMORY;
        }

    ///////////////////
    // Attach to Viper context flow
    
    if (SUCCEEDED(hr))
        {
        hr = ViperAttachIntrinsicsToContext
            (
            m_pAppln,
            m_pSession,
            m_pRequest,
            m_pResponse,
            m_pServer
            );
        }

    ///////////////////
    // Execute

    BOOL fSkipExecute = FALSE; // (need to skip if session-less)

    if (SUCCEEDED(hr))
        {
        CTemplate *pTemplate = NULL;

        if (FIsBrowserRequest())
            {
#ifndef PERF_DISABLE
            g_PerfData.Incr_REQBROWSEREXEC();
#endif
            // Init Response and Server for compiler errors
            m_pResponse->ReInit(m_pIReq, NULL, m_pRequest, NULL, NULL, this);
            m_pRequest->ReInit(m_pIReq, this);
            m_pServer->ReInit(m_pIReq, this);
            
            // Load the script - cache will AddRef
            hr = LoadTemplate(m_pIReq->QueryPszPathTranslated(), this, 
                              &pTemplate, intrinsics,
                              FALSE /* !GlobalAsa */, &fTemplateInCache);

            // In case of ACL on the file (or directory) make sure
            // we don't assume that AppOnStart succeeded on the
            // first try (without the correct impersonation). Setting
            // m_fApplnOnStartFailed will force another try, with the
            // correct impersonation.
            if (fFirstAppRequest && FAILED(hr))
                m_fApplnOnStartFailed = TRUE;

            // Take care of is session-less templates
            if (SUCCEEDED(hr) && !pTemplate->FSession())
                {
                
                // UNDONE - Remove the reposting logic. Bug 301311.
                //
                // The problem is that we will leak the wam request and 
                // cause all sorts of trouble with keep alives. It seems 
                // like a marginal cost to just allow the first request to
                // run on the session activity. We could also remove the
                // StartISAThreadBracket restriction on nesting, but that
                // seems like it might open up the door to making some
                // real errors.

                /*
                // The problem only occurs the first time the
                // template is loaded. After that there's a
                // look-ahead in BrowserRequestInit()
                
                // if the template wasn't cached before we could
                // be running on session activity
                if (m_ecsActivityScope == csSession)
                    {
                    // Repost with it's own activity
                    hr = NewBrowserRequest(m_pIReq);
                    fSkipExecute = TRUE;

                    // Mark this request as DONE_WITH_SESSION so that
                    // it will not be forced later. We don't need it
                    // because we posted another HitObj with the
                    // same WAM_EXEC_INFO
                    m_fDoneWithSession = TRUE;
                    }
                else 
                */
                
                if (m_pSession)
                    {
                    // Activity is alright (most likely 
                    // application level) but still there's
                    // a session attached -> hide it
                    m_fHideSessionIntrinsic = TRUE;
                    }
                }
                
            // Take care of the 449 processing (most likely already done on I/O thread)
            if (SUCCEEDED(hr) && !m_fDoneWithSession)
                {
                pTemplate->Do449Processing(this);
                if (m_fDoneWithSession)
                    fSkipExecute = TRUE;  // 449 sent the response
                }
            }

        if (SUCCEEDED(hr) && !fSkipExecute)
            {
            // Execute script
            MFS_START(memrfs)
            hr = Execute(pTemplate, this, intrinsics);
            MFS_END_HR(memrfs)
            
            // OnEndPage
            if (m_pPageObjMgr)
                m_pPageObjMgr->OnEndPageAllObjects();
            }

        // Release the template
        if (pTemplate)
            pTemplate->Release();

        if (FIsBrowserRequest())
            {
            if (!fSkipExecute)
                {
                // Flush response after completing execution
                m_pResponse->FinalFlush(hr);
                }

#ifndef PERF_DISABLE
            g_PerfData.Decr_REQBROWSEREXEC();
#endif
            }
        else if (FIsSessionCleanupRequest())
            {
            // Remove session
            if (m_pSession)
                {
                m_pSession->UnInit();
                m_pSession->Release();
                m_pSession = NULL;
                }
            }
        else if (FIsApplnCleanupRequest())
            {
            // Remove application
            if ( m_pAppln )
                {
                m_pAppln->UnInit();
                m_pAppln->Release();
                m_pAppln = NULL;
                }
            }
        }

    ///////////////////
    // Release Scripting Context
    
    if (m_pScriptingContext)
        {
        m_pScriptingContext->Release();
        m_pScriptingContext = NULL;
        }
        
    ///////////////////
    // Do The Perf Counters

#ifndef PERF_DISABLE
    DWORD dwtExecTime = ElapsedTimeSinceTimestamp();

    if (!fSkipExecute && FIsBrowserRequest())
        {
        g_PerfData.Incr_REQPERSEC();
        g_PerfData.Set_REQEXECTIME(dwtExecTime);
    
        switch (m_eExecStatus)
            {
        case eExecSucceeded:
            if (m_pResponse->FWriteClientError())
                {
                g_PerfData.Incr_REQCOMFAILED();
                g_PerfData.Incr_REQERRORPERSEC();
                }
            else
                {
                g_PerfData.Incr_REQSUCCEEDED();
                }
            break;
            
        case eExecFailed:
            if (hr == E_USER_LACKS_PERMISSIONS)
                {
                g_PerfData.Incr_REQNOTAUTH();
                }
            else if (FIsPreprocessorError(hr))
                {
                g_PerfData.Incr_REQERRPREPROC();
                }
            else if (m_fCompilationFailed)
                {
                g_PerfData.Incr_REQERRCOMPILE();
                }
            else
                {
                g_PerfData.Incr_REQERRRUNTIME();
                }
        
            g_PerfData.Incr_REQFAILED();
            g_PerfData.Incr_REQERRORPERSEC();
            break;
            
        case eExecTimedOut:
            g_PerfData.Incr_REQTIMEOUT();
            break;
            }
        }
#endif    

    ///////////////////
    // Cleanup after first application request
    
    if (fFirstAppRequest && !m_fApplnOnStartFailed && !fSkipExecute)
        m_pAppln->SetFirstRequestRan();

    if (fApplnLocked)
        m_pAppln->InternalUnLock();
    
    ///////////////////
    // make sure script didn't leave application locked

    if (!FIsApplnCleanupRequest())
        m_pAppln->UnLockAfterRequest();

    ///////////////////
    // In order not to refer to intrinsics later
    // remove page component collection
    
    StopComponentProcessing();

	// Unset the impersonation
	if (FIsWinNT())
		SetThreadToken(&hThread, NULL);
                
    ///////////////////
    // Point session to NULL HitObj

    if (m_pSession)
        m_pSession->SetHitObj(NULL);

    LeaveThreadGate();   // notify the thead gate
    m_fExecuting = FALSE;
    
    return hr;
    }

/*===================================================================
CHitObj::ExecuteChildRequest

Executes child browser request

Parameters:
    fTransfer       -- flag -- End execution after this
    szTemplate      -- filename of the template to execute
    szVirtTemplate  -- virt path to template

Returns:
    S_OK
===================================================================*/
HRESULT CHitObj::ExecuteChildRequest
(
BOOL fTransfer, 
TCHAR *szTemplate,
TCHAR *szVirtTemplate
)
    {
    HRESULT hr = S_OK;

    // Prepare the new intrinsics structure (with the new scripting namespace)
    CIntrinsicObjects intrinsics;
    intrinsics.PrepareChild(m_pResponse, m_pRequest, m_pServer);
    
    TCHAR *saved_m_szCurrTemplateVirtPath = m_szCurrTemplateVirtPath;
    TCHAR *saved_m_szCurrTemplatePhysPath = m_szCurrTemplatePhysPath;
    // these two fields used for compilation and error reporting
    m_szCurrTemplateVirtPath = szVirtTemplate;
    m_szCurrTemplatePhysPath = szTemplate;
    
    // Load the template from cache
    CTemplate *pTemplate = NULL;
    BOOL fTemplateInCache;
    hr = g_TemplateCache.Load(FALSE, szTemplate, DWInstanceID(), this, &pTemplate, &fTemplateInCache);

    if (FAILED(hr))
        {
        if (pTemplate)
            {
            pTemplate->Release();
            pTemplate = NULL;
            }

        m_szCurrTemplateVirtPath = saved_m_szCurrTemplateVirtPath;
        m_szCurrTemplatePhysPath = saved_m_szCurrTemplatePhysPath;

        // to tell the server object to display the correct error message
        return E_COULDNT_OPEN_SOURCE_FILE;
        }

    // Save HitObj's execution state info
    CComponentCollection  *saved_m_pPageCompCol           = m_pPageCompCol;
    CPageComponentManager *saved_m_pPageObjMgr            = m_pPageObjMgr;
    IUnknown              *saved_m_punkScriptingNamespace = m_punkScriptingNamespace;
    DWORD                 saved_m_dwObjectContextCookie   = m_dwObjectContextCookie;
    ActiveEngineInfo      *saved_m_pEngineInfo            = m_pEngineInfo;
    IDispatch             *saved_m_pdispTypeLibWrapper    = m_pdispTypeLibWrapper;

    CTemplate *saved_pTemplate = m_pResponse->SwapTemplate(pTemplate);
    void *saved_pvEngineInfo   = m_pResponse->SwapScriptEngineInfo(NULL);

    // Re-Init the saved state
    m_pPageCompCol = NULL;
    m_pPageObjMgr = NULL;
    m_punkScriptingNamespace = NULL;
    m_dwObjectContextCookie = NULL_GIP_COOKIE;
    m_pEngineInfo = NULL;
    m_pdispTypeLibWrapper = NULL;

    // Create child request components framework
    hr = InitComponentProcessing();

    // Execute
    if (SUCCEEDED(hr))
        {
		// Set status code to 500 in error cases.
		if (FHasASPError())
			m_pResponse->put_Status(L"500 Internal Server Error");

        // Execute [child] script
        hr = ::Execute(pTemplate, this, intrinsics, TRUE);
        
        // OnEndPage
        if (m_pPageObjMgr)
            m_pPageObjMgr->OnEndPageAllObjects();
        }

    // Clean-out new components framework
    StopComponentProcessing();

    // Restore HitObj's execution state info
    m_pPageCompCol           = saved_m_pPageCompCol;
    m_pPageObjMgr            = saved_m_pPageObjMgr;
    m_punkScriptingNamespace = saved_m_punkScriptingNamespace;
    m_dwObjectContextCookie  = saved_m_dwObjectContextCookie;
    m_pEngineInfo            = saved_m_pEngineInfo;
    SetTypeLibWrapper(saved_m_pdispTypeLibWrapper);
    m_pResponse->SwapTemplate(saved_pTemplate);
    m_pResponse->SwapScriptEngineInfo(saved_pvEngineInfo);
    m_szCurrTemplateVirtPath = saved_m_szCurrTemplateVirtPath;
    m_szCurrTemplatePhysPath = saved_m_szCurrTemplatePhysPath;

    // Cleanup
    if (pTemplate)
        pTemplate->Release();

    if (m_pResponse->FResponseAborted() || fTransfer || FHasASPError())
        {
        // propagate Response.End up the script engine chain
        m_pResponse->End();
        }

    // Done
    return hr;
    }

/*===================================================================
CHitObj::GetASPError

Get ASP Error object. Used for Server.GetLastError()

Parameters
    ppASPError  [out] addref'd error object (new or old)
    
Returns
    HRESULT
===================================================================*/
HRESULT CHitObj::GetASPError
(
IASPError **ppASPError
)
    {
    Assert(ppASPError);
    
    if (m_pASPError == NULL)
        {
        // return bogus one
        *ppASPError = new CASPError;
        return (*ppASPError != NULL) ? S_OK : E_OUTOFMEMORY;
        }
        
    m_pASPError->AddRef();      // return addref'd
    *ppASPError = m_pASPError;
    return S_OK;
    }

/*===================================================================
CHitObj::RejectBrowserRequestWhenNeeded

Request reject-before-execution-started logic

Parameters:
    dwtQueueWaitTime    time request waited in the queue, ms
    pfRejected          OUT flag -- TRUE if rejected

Returns:
    S_OK
===================================================================*/
HRESULT CHitObj::RejectBrowserRequestWhenNeeded
(
DWORD dwtQueueWaitTime,
BOOL *pfRejected
)
    {
    Assert(FIsBrowserRequest());
    
    UINT wError = 0;
    
    // If shutting down
    if (IsShutDownInProgress())
        {
        wError = IDE_SERVER_SHUTTING_DOWN;
        }
        
    // If waited long enough need to check if still connected
    if (wError == 0)
        {
        DWORD dwConnTestSec = m_pAppln->QueryAppConfig()->dwQueueConnectionTestTime();
        
        if (dwConnTestSec != 0xffffffff && dwConnTestSec != 0)
            {
            if (dwtQueueWaitTime > (dwConnTestSec * 1000))
                {
                BOOL fConnected = TRUE;
                if (m_pIReq)
                    m_pIReq->TestConnection(&fConnected);

                // if client disconnected -- respond with 'Server Error'
                if (!fConnected)
                    {
                    wError = IDE_500_SERVER_ERROR;
#ifndef PERF_DISABLE
                    g_PerfData.Incr_REQCOMFAILED();
#endif                    
                    }
                }
            }
        }
    
    // If waited too long -- reject
    if (wError == 0)
        {
        DWORD dwTimeOutSec = m_pAppln->QueryAppConfig()->dwQueueTimeout();
        
        if (dwTimeOutSec != 0xffffffff && dwTimeOutSec != 0)
            {
            if (dwtQueueWaitTime > (dwTimeOutSec * 1000))
                {
                wError = IDE_SERVER_TOO_BUSY;
#ifndef PERF_DISABLE
                g_PerfData.Incr_REQREJECTED();
#endif
                }
            }
        }

    if (wError)
        {
        m_fExecuting = FALSE; // before 'report error' to disable transfer
        ReportServerError(wError);
        *pfRejected = TRUE;
        }
    else
        {
        *pfRejected = FALSE;
        }

    return S_OK;
    }

/*===================================================================
CHitObj::ReportServerError

Report server error without using the response object

Parameters:
    ErrorID     error message id

Returns:

Side effects:
    None.
===================================================================*/
HRESULT CHitObj::ReportServerError
(
UINT ErrorId
)
    {
    // do nothing on non-browser requests or if no WAM_EXEC_INFO
    if (!FIsBrowserRequest() || m_pIReq == NULL)
        return S_OK;

    DWORD dwRequestStatus = HSE_STATUS_ERROR;

    if (ErrorId)
        {
        Handle500Error(ErrorId, m_pIReq);
        }
            
    m_pIReq->ServerSupportFunction
        (
        HSE_REQ_DONE_WITH_SESSION,
        &dwRequestStatus,
        0,
        NULL
        );

    SetDoneWithSession();
    return S_OK;
    }

#ifdef DBG
/*===================================================================
CHitObj::AssertValid

Test to make sure that the CHitObj object is currently correctly formed
and assert if it is not.

Returns:

Side effects:
    None.
===================================================================*/
VOID CHitObj::AssertValid() const
    {
    Assert(m_fInited);
    Assert(FIsValidRequestType());
    if (FIsBrowserRequest())
        {
        Assert(m_pIReq != NULL);
        Assert(m_pPageCompCol != NULL );
        Assert(m_pPageObjMgr != NULL);
        }
    }
#endif // DBG

/*===================================================================
CHitObj::AddObjectContext
===================================================================*/
void CHitObj::AddObjectContext(IASPObjectContext *pASPObjContext)
    {
    HRESULT hr = S_OK;  
    Assert(m_dwObjectContextCookie == NULL_GIP_COOKIE);
    Assert(pASPObjContext);

    if (g_pGIT == 0) {
        hr = CoCreateInstance(CLSID_StdGlobalInterfaceTable, 0, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (void**) &g_pGIT);
        Assert(hr == S_OK);
    }
    hr = g_pGIT->RegisterInterfaceInGlobal(pASPObjContext, IID_IASPObjectContext, &m_dwObjectContextCookie);
	if (hr != S_OK)
		DBGPRINTF((DBG_CONTEXT, "RegisterInterfaceInGlobal failed, hr = 0x%08X\n", hr));

    Assert(hr == S_OK);
    Assert(m_dwObjectContextCookie != NULL_GIP_COOKIE);
    }
    
/*===================================================================
CHitObj::RemoveObjectContext
===================================================================*/
void CHitObj::RemoveObjectContext()
    {
    HRESULT hr = S_OK;  
    if (m_dwObjectContextCookie != NULL_GIP_COOKIE)
        {
        hr = g_pGIT->RevokeInterfaceFromGlobal(m_dwObjectContextCookie);
		if (hr != S_OK)
			DBGPRINTF((DBG_CONTEXT, "RevokeInterfaceFromGlobal failed, hr = 0x%08X\n", hr));
        m_dwObjectContextCookie = NULL_GIP_COOKIE;
        }
    }

/*===================================================================
CHitObj::UseObjectContext
===================================================================*/
void CHitObj::UseObjectContext(IASPObjectContext** ppASPObjectContext)
    {
    HRESULT hr = S_OK;  
    if (m_dwObjectContextCookie != NULL_GIP_COOKIE)
        {
        hr = g_pGIT->GetInterfaceFromGlobal(m_dwObjectContextCookie, IID_IASPObjectContext, (void**) ppASPObjectContext);
        if (hr != S_OK) 
            {
			DBGPRINTF((DBG_CONTEXT, "GetInterfaceFromGlobal failed, hr = 0x%08X\n", hr));
            *ppASPObjectContext = NULL;
            }
        }
    else
        {
        DBG_ASSERT( g_pIASPObjectContextZombie );

        hr = g_pIASPObjectContextZombie->QueryInterface(
                    IID_IASPObjectContext,
                    reinterpret_cast<void **>(ppASPObjectContext)
                    );
        
        DBG_ASSERT(SUCCEEDED(hr));
        }
    }