/*=================================================================== 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; IASPObjectContext *g_pIASPDummyObjectContext = 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_pASPObjectContext(NULL), m_punkScriptingNamespace(NULL), 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) { m_uCodePage = GetACP(); } /*=================================================================== 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 == CP_ACP ? GetACP() : 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 == S_OK) // 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: + CCH_SESSION_ID_COOKIE + = + + '\0') 50 + 20 + 1 + SESSIONID_LEN + 1 NOTE we arbitrarily allow 50 bytes for 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; } 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 , 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(m_pResponse); } break; case itRequest: if (_wcsicmp(pwszName, WSZ_OBJ_REQUEST) == 0) { fNameMatch = TRUE; if (!m_fHideRequestAndResponseIntrinsics) *ppUnk = static_cast(m_pRequest); } break; case itSession: if (_wcsicmp(pwszName, WSZ_OBJ_SESSION) == 0) { fNameMatch = TRUE; if (!m_fHideSessionIntrinsic) *ppUnk = static_cast(m_pSession); } break; case itServer: if (_wcsicmp(pwszName, WSZ_OBJ_SERVER) == 0) { fNameMatch = TRUE; *ppUnk = static_cast(m_pServer); } break; case itAppln: if (_wcsicmp(pwszName, WSZ_OBJ_APPLICATION) == 0) { fNameMatch = TRUE; *ppUnk = static_cast(m_pAppln); } break; case itObjContext: if (_wcsicmp(pwszName, WSZ_OBJ_OBJECTCONTEXT) == 0) { // if there isn't an ASPObjectContext, then most likely // the asp script is asking for the object context on a // non-transacted page. Return the Dummy Object Context // which will allow ASP to return a friendly error saying // that ObjectContext is not available rather than // ELEMENT_NOT_FOUND. if (m_pASPObjectContext == NULL) { if (g_pIASPDummyObjectContext == NULL) { CASPDummyObjectContext *pContext = new CASPDummyObjectContext(); if (pContext == NULL) { return E_OUTOFMEMORY; } g_pIASPDummyObjectContext = static_cast(pContext); } *ppUnk = g_pIASPDummyObjectContext; } else { *ppUnk = static_cast(m_pASPObjectContext); } 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; 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; 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_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_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