mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2919 lines
79 KiB
2919 lines
79 KiB
/*===================================================================
|
|
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: <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;
|
|
}
|
|
|
|
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) {
|
|
|
|
// 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<IASPObjectContext *>(pContext);
|
|
}
|
|
*ppUnk = g_pIASPDummyObjectContext;
|
|
}
|
|
else {
|
|
|
|
*ppUnk = static_cast<IASPObjectContext *>(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
|