Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1521 lines
46 KiB

/*===================================================================
Microsoft Denali
Microsoft Confidential.
Copyright 1996 Microsoft Corporation. All Rights Reserved.
Component: Executor
Owner: DGottner
File: executor.cpp
This file contains the executor, whose job is to co-ordinate the
execution of Denali scripts.
===================================================================*/
#include "denpre.h"
#pragma hdrstop
#include "exec.h"
#include "response.h"
#include "request.h"
#include "perfdata.h"
#include "memchk.h"
#include <iismsg.h>
// Local declarations
HRESULT ExecuteGlobal(CHitObj *pHitObj,
const CIntrinsicObjects &intrinsics,
ActiveEngineInfo *pEngineInfo);
HRESULT ExecuteRequest(CTemplate *pTemplate, CHitObj *pHitObj,
const CIntrinsicObjects &intrinsics,
ActiveEngineInfo *pEngineInfo);
HRESULT ReInitIntrinsics(CHitObj *pHitObj, const CIntrinsicObjects &intrinsics,
ActiveEngineInfo *pEngineInfo, BOOL fPostGlobal);
HRESULT AllocAndLoadEngines(CHitObj *pHitObj, CTemplate *pTemplate, ActiveEngineInfo *pEngineInfo,
CScriptingNamespace *pScriptingNamespace, BOOL fGlobalAsa);
VOID DeAllocAndFreeEngines(ActiveEngineInfo *pEngineInfo, CAppln *pAppln);
CScriptEngine *GetScriptEngine(int iScriptEngine, void *pvData);
HRESULT CallScriptFunctionOfEngine(ActiveEngineInfo &engineInfo, short iScriptBlock, wchar_t *strFunction, CASPObjectContext *pASPObjectContext = NULL);
HRESULT CallScriptFunction(ActiveEngineInfo &engineInfo, wchar_t *strFunction);
HRESULT TestScriptFunction(ActiveEngineInfo &engineInfo, wchar_t *strFunction);
/*===================================================================
Execute
Execute a request:
First determine if Global needs to be called
then invoke actual requested template
Parameters:
pTemplate - pointer to loaded template (could be NULL)
pHitObj - pointer to the hit object
intrinsics - pointers to the intrinsic IUnknown pointers.
fChild - flag: TRUE when child request (Server.Execute())
Returns:
S_OK on success
===================================================================*/
HRESULT Execute
(
CTemplate *pTemplate,
CHitObj *pHitObj,
const CIntrinsicObjects &intrinsics,
BOOL fChild
)
{
HRESULT hr = S_OK;
ActiveEngineInfo engineInfo;
BOOL fRanGlobal = FALSE;
// The hit obj must be valid
Assert(pHitObj != NULL);
pHitObj->AssertValid();
// Check for valid Session codepage. We do it here, rather than in CSession::Init in
// order to avoid generic "New Session Failed" message.
if (pHitObj->GetCodePage() != CP_ACP && !IsValidCodePage(pHitObj->GetCodePage()))
{
HandleErrorMissingFilename(IDE_BAD_CODEPAGE_IN_MB, pHitObj);
return E_FAIL;
}
// Give the engine list to the hitobject
pHitObj->SetActiveEngineInfo(&engineInfo);
/*
* If there is a Global.ASA, call it
*/
if (pHitObj->GlobalAspPath() && !fChild)
{
// Clear out the engine info
engineInfo.cEngines = 0;
engineInfo.cActiveEngines = 0;
engineInfo.rgActiveEngines = NULL;
// Init the intrinsics
hr = ReInitIntrinsics(pHitObj, intrinsics, &engineInfo, /* fPostGlobal*/ FALSE);
if (FAILED(hr))
return(hr);
hr = ExecuteGlobal(pHitObj, intrinsics, &engineInfo);
if (intrinsics.PResponse() && intrinsics.PResponse()->FResponseAborted())
{
hr = S_OK;
goto LExit;
}
if (E_SOURCE_FILE_IS_EMPTY == hr)
// bug 977: silently ignore empty global.asa file
hr = S_OK;
else if (FAILED(hr))
{
// Bug 481: If global.asa fails due to Response.End (or Response.Redirect),
// then halt execution of the calling script. If the
// script fails due to Response.End, then return OK status
//
if (hr == DISP_E_EXCEPTION)
hr = S_OK;
// In any case, blow out of here
goto LExit;
}
// Running Global.asa added the scripting namespace to the hitobj. This will cause us problems
// later, remove it.
pHitObj->RemoveScriptingNamespace();
fRanGlobal = TRUE;
}
/*
* If this is not a browser request, then we are done
* For non-browser requests, we do want to run Global.asa (if any), but there is no real template to run.
*/
if (!pHitObj->FIsBrowserRequest())
{
hr = S_OK;
goto LExit;
}
// Clear out (or re-clear out) the engine info
engineInfo.cEngines = 0;
engineInfo.cActiveEngines = 0;
engineInfo.rgActiveEngines = NULL;
// Init or Re-Init the intrinsics
ReInitIntrinsics(pHitObj, intrinsics, &engineInfo, fRanGlobal || fChild);
if (!fChild)
{
// For non-child requests hand new Template to Response object
// (for child requests already done)
intrinsics.PResponse()->ReInitTemplate(pTemplate, pHitObj->PSzNewSessionCookie());
}
else
{
// For child requests hand new engine info to the response object
intrinsics.PResponse()->SwapScriptEngineInfo(&engineInfo);
}
// Run the main template
if (pTemplate->FScriptless() && !pHitObj->PAppln()->FDebuggable())
{
// special case scriptless pages
hr = intrinsics.PResponse()->WriteBlock(0);
}
else
{
hr = ExecuteRequest(pTemplate, pHitObj, intrinsics, &engineInfo);
}
LExit:
intrinsics.PResponse()->SwapScriptEngineInfo(NULL);
pHitObj->SetActiveEngineInfo(NULL);
return hr;
}
/*===================================================================
ExecRequest
Execute a request for an actual template (not Global.asa)
execute a request by
- getting the script name
- loading the script into memory
- interpreting the opcodes
Parameters:
pTemplate - pointer to loaded template
pHitObj - pointer to the hit object
intrinsics - pointers to the intrinsic IUnknown pointers.
pEngineInfo - pointers to engine info
Returns:
S_OK on success
===================================================================*/
HRESULT ExecuteRequest(CTemplate *pTemplate,
CHitObj *pHitObj,
const CIntrinsicObjects &intrinsics,
ActiveEngineInfo *pEngineInfo)
{
HRESULT hr = S_OK;
BOOL fAborted = FALSE;
BOOLB fDebuggerNotifiedOnStart = FALSE;
BOOL fServiceDomainEnterred = FALSE;
CASPObjectContext *pASPObjectContext = NULL;
CASPObjectContext *pPoppedASPObjectContext = NULL;
#ifndef PERF_DISABLE
BOOLB fPerfTransPending = FALSE;
#endif
// The template must be valid
Assert(pTemplate);
// The hit obj must be valid
Assert(pHitObj != NULL);
pHitObj->AssertValid();
// This function should never be called on a non-browser request
Assert(pHitObj->FIsBrowserRequest());
// Remember template's type library wrapper with the HitObj
if (pTemplate->PTypeLibWrapper())
pHitObj->SetTypeLibWrapper(pTemplate->PTypeLibWrapper());
if (pTemplate->FTransacted()) {
#ifndef PERF_DISABLE
g_PerfData.Incr_TRANSTOTAL();
g_PerfData.Incr_TRANSPENDING();
fPerfTransPending = TRUE;
#endif
pASPObjectContext = new CASPObjectContext();
if (!pASPObjectContext) {
hr = E_OUTOFMEMORY;
goto LExit;
}
pPoppedASPObjectContext = pHitObj->SetASPObjectContext(pASPObjectContext);
}
if (pTemplate->PServicesConfig()) {
hr = CoEnterServiceDomain(pTemplate->PServicesConfig());
if (FAILED(hr)) {
goto LExit;
}
fServiceDomainEnterred = TRUE;
}
// load script engines
hr = AllocAndLoadEngines(pHitObj, pTemplate, pEngineInfo, intrinsics.PScriptingNamespace(), /* fGlobalAsa */FALSE);
if (FAILED(hr)) {
pHitObj->SetCompilationFailed();
goto LExit;
}
// If debugging, notify debugger ONPAGESTART
// BUG 138773: Notify debugger AFTER scripts load
// (script must be in running state when AttachTo() is called because debugger may want a code context)
//
if (pHitObj->PAppln()->FDebuggable()) {
pTemplate->AttachTo(pHitObj->PAppln());
if (SUCCEEDED(pTemplate->NotifyDebuggerOnPageEvent(TRUE)))
fDebuggerNotifiedOnStart = TRUE;
}
// bug 1009: if no script engines, do not attempt to do anything
if(0 == pTemplate->CountScriptEngines())
goto LExit;
// run the script by calling primary script engine's global code
hr = CallScriptFunctionOfEngine(*pEngineInfo, // engine-info
0, // primary script engine
NULL, // call the engine's global code
pHitObj->PASPObjectContext());
if (fServiceDomainEnterred) {
fServiceDomainEnterred = FALSE;
CoLeaveServiceDomain(static_cast<ITransactionStatus *>(pHitObj->PASPObjectContext()));
}
if (pTemplate->FTransacted()) {
fAborted = pHitObj->PASPObjectContext()->FAborted();
}
if (FAILED(hr)) {
/*
* The cryptically named CONTEXT_E_OLDREF error in this case means that
* we are trying to run a transacted web page, but DTC isnt running.
* CONTEXT_E_TMNOTAVAILABLE means the same thing. God knows why
*/
if (hr == CONTEXT_E_OLDREF || hr == CONTEXT_E_TMNOTAVAILABLE) {
HandleErrorMissingFilename(IDE_EXECUTOR_DTC_NOT_RUNNING, pHitObj);
}
// Regardless of the error, exit
goto LExit;
}
/*
* If this is a transacted web page, then run either the OnTransactionCommit
* or OnTransactionAbort method in the script, if any.
*
* If the script writer did an explicit SetAbort, or a component run by the script
* did a SetAbort, then we run OnTransactionAbort, otherwise run OnTransactionCommit
*/
if (pTemplate->FTransacted()) {
#ifndef PERF_DISABLE
g_PerfData.Incr_TRANSPERSEC();
#endif
if (fAborted) {
hr = CallScriptFunction(*pEngineInfo, L"OnTransactionAbort");
#ifndef PERF_DISABLE
g_PerfData.Incr_TRANSABORTED();
#endif
}
else {
hr = CallScriptFunction(*pEngineInfo, L"OnTransactionCommit");
#ifndef PERF_DISABLE
g_PerfData.Incr_TRANSCOMMIT();
#endif
}
// Ignore UNKNOWNNAME -- this means the author didnt write the method, which is fine
if (hr == DISP_E_UNKNOWNNAME || hr == DISP_E_MEMBERNOTFOUND)
hr = S_OK;
if (FAILED(hr))
goto LExit;
}
LExit:
//
// Return the engine(s) to cache. Be sure to do this before leaving the services domain
// after we have deallocated the freed the engines or else the context will leak Variant objects
// as the reset script code will not make it to the various variant allocated during the running of the script.
//
DeAllocAndFreeEngines(pEngineInfo, pHitObj->PAppln());
if (fServiceDomainEnterred) {
CoLeaveServiceDomain(NULL);
}
#ifndef PERF_DISABLE
if (fPerfTransPending)
g_PerfData.Decr_TRANSPENDING();
#endif
// Uninit the scripting namespace
(VOID)intrinsics.PScriptingNamespace()->UnInit();
// If debugging, notify debugger ONPAGEDONE
if (fDebuggerNotifiedOnStart) {
Assert(pHitObj->PAppln()->FDebuggable());
pTemplate->NotifyDebuggerOnPageEvent(FALSE);
}
if (pPoppedASPObjectContext)
pHitObj->SetASPObjectContext(pPoppedASPObjectContext);
if (pASPObjectContext)
pASPObjectContext->Release();
return hr;
}
/*===================================================================
ExecuteGlobal
UNDONE: handle script engine the same manner as mainline script engines
with respect to debugging.
Execute code in Global.ASA as part of application or session start or end.
Parameters:
pHitObj - pointer to the hit object
intrinsics - pointers to the intrinsic IUnknown pointers.
pEngineInfo - pointers to engine info
pfDeleteSession - true if global.asa failed, and therefore the caller should
Returns:
S_OK on success
===================================================================*/
HRESULT ExecuteGlobal
(
CHitObj *pHitObj,
const CIntrinsicObjects &intrinsics,
ActiveEngineInfo *pEngineInfo
)
{
HRESULT hr = S_OK;
CTemplate *pTemplate = NULL;
WORD iEng;
BOOLB fDebuggerNotifiedOnStart = FALSE;
BOOL fUnHideRequestAndResponse = FALSE;
BOOL fOnStartAppln = FALSE;
BOOL fOnEndAppln = FALSE;
BOOL fOnEndSession = FALSE;
BOOL fGlobalAsaInCache;
BOOL fApplnStarted = FALSE;
BOOL fServiceDomainEnterred = FALSE;
CASPObjectContext *pASPObjectContext = NULL;
CASPObjectContext *pPoppedASPObjectContext = NULL;
UINT savedCodePage = CP_ACP;
UINT loadedCodePage = CP_ACP;
// The hit obj must be there, be valid, & have a global.asa name
Assert(pHitObj != NULL);
pHitObj->AssertValid();
Assert(pHitObj->GlobalAspPath() != NULL && *(pHitObj->GlobalAspPath()) != '\0');
// Other arg's must be right
Assert(pEngineInfo != NULL);
// save the current code page in case LoadTemplate changes it...
savedCodePage = pHitObj->GetCodePage();
// Load the script - cache will AddRef
// bug 1051: load template before possibly removing response object (in switch block, below),
// so error reporting to browser will work
hr = LoadTemplate(pHitObj->GlobalAspPath(), pHitObj, &pTemplate, intrinsics, /* fGlobalAsa */ TRUE, &fGlobalAsaInCache);
if (FAILED(hr))
goto LExit;
// get the codepage again so that we can later compare to see if the initial code
// page should be reverted
loadedCodePage = pHitObj->GetCodePage();
Assert(pTemplate != NULL);
// Remember GLOBAL.ASA's type library wrapper with the application
// on the first request
if (pHitObj->FStartApplication() && pTemplate->PTypeLibWrapper())
{
pHitObj->PAppln()->SetGlobTypeLibWrapper(pTemplate->PTypeLibWrapper());
}
if (pTemplate->FTransacted()) {
pASPObjectContext = new CASPObjectContext();
if (!pASPObjectContext) {
hr = E_OUTOFMEMORY;
goto LExit;
}
pPoppedASPObjectContext = pHitObj->SetASPObjectContext(pASPObjectContext);
}
if (pTemplate->PServicesConfig()) {
hr = CoEnterServiceDomain(pTemplate->PServicesConfig());
if (FAILED(hr)) {
goto LExit;
}
fServiceDomainEnterred = TRUE;
}
Assert(pHitObj->FIsValidRequestType());
// Figure out which events to trigger
if (pHitObj->FIsBrowserRequest())
{
fOnStartAppln = pHitObj->FStartApplication();
if (fOnStartAppln)
{
// Hide response and request intrinsics from namespace
pHitObj->HideRequestAndResponseIntrinsics();
// Flag that intrinsics need to be un-hidden back in.
fUnHideRequestAndResponse = TRUE;
}
}
else if (pHitObj->FIsSessionCleanupRequest())
{
fOnEndSession = TRUE;
}
else if (pHitObj->FIsApplnCleanupRequest())
{
fOnEndAppln = TRUE;
}
// If debugging, notify debugger ONPAGESTART
if (pHitObj->PAppln()->FDebuggable())
{
if (SUCCEEDED(pTemplate->NotifyDebuggerOnPageEvent(TRUE)))
fDebuggerNotifiedOnStart = TRUE;
}
hr = AllocAndLoadEngines(pHitObj, pTemplate, pEngineInfo, intrinsics.PScriptingNamespace(), /* fGlobalAsa */TRUE);
if (FAILED(hr))
goto LExit;
// BUG 93991: Defer registration of new document with debugger until after script engines have
// been loaded
//
if (!fGlobalAsaInCache && pHitObj->PAppln()->FDebuggable())
pTemplate->AttachTo(pHitObj->PAppln());
// bug 975: if no script engines, do not attempt to call event functions
if(0 == pTemplate->CountScriptEngines())
goto LExit;
/*
* Call event functions as required
* bug 459: event functions may be in any script engine
*/
// First run Application_OnStart
if (fOnStartAppln)
{
pHitObj->SetEventState(eEventAppOnStart);
hr = CallScriptFunction(*pEngineInfo, L"Application_OnStart");
if (SUCCEEDED(hr) || hr == DISP_E_UNKNOWNNAME || hr == DISP_E_MEMBERNOTFOUND ||
intrinsics.PResponse()->FResponseAborted())
{
if (fUnHideRequestAndResponse)
{
pHitObj->UnHideRequestAndResponseIntrinsics();
fUnHideRequestAndResponse = FALSE;
}
fApplnStarted = TRUE;
hr = S_OK;
}
else
{
goto LExit;
}
}
if (pHitObj->FStartSession())
{
// If application on start was run, add Response and Request names to script engines
if (fOnStartAppln)
{
for (iEng = 0; iEng < pEngineInfo->cActiveEngines; ++iEng)
{
if (FAILED(hr = pEngineInfo->rgActiveEngines[iEng].pScriptEngine->AddAdditionalObject(WSZ_OBJ_RESPONSE, FALSE)))
goto LExit;
if (FAILED(hr = pEngineInfo->rgActiveEngines[iEng].pScriptEngine->AddAdditionalObject(WSZ_OBJ_REQUEST, FALSE)))
goto LExit;
}
}
pHitObj->SetEventState(eEventSesOnStart);
hr = CallScriptFunction(*pEngineInfo, L"Session_OnStart");
if (FAILED(hr) && hr != DISP_E_UNKNOWNNAME && hr != DISP_E_MEMBERNOTFOUND &&
!intrinsics.PResponse()->FResponseAborted())
{
// Mark session as on-start-failed - to be deleted soon
pHitObj->SessionOnStartFailed();
}
else
{
if (SUCCEEDED(hr))
{
// Mark as on-start-invoked -- need to wait for timeout
pHitObj->SessionOnStartInvoked();
}
// Check if Session_OnEnd Present
if (SUCCEEDED(TestScriptFunction(*pEngineInfo, L"Session_OnEnd")))
{
// Mark as on-end-present -- need to execute OnEnd later
pHitObj->SessionOnEndPresent();
}
hr = S_OK;
}
goto LExit;
}
BOOL fImpersonationSet = FALSE;
BOOL fExecuteOnEnd = TRUE;
// no need to even attempt to run Session_OnEnd if it isn't present
if (fOnEndSession && FAILED(TestScriptFunction(*pEngineInfo, L"Session_OnEnd")))
fOnEndSession = FALSE;
// same for application_onend
if (fOnEndAppln && FAILED(TestScriptFunction(*pEngineInfo, L"Application_OnEnd")))
fOnEndAppln = FALSE;
if (fOnEndSession || fOnEndAppln) {
// Is the OnEnd routine supposed to be executed as the Anonymous user?
if (pHitObj->PAppln()->QueryAppConfig()->fRunOnEndAsAnon()) {
// if so, but we don't have a valid token, then we can't
if (pHitObj->PAppln()->QueryAppConfig()->AnonToken() == INVALID_HANDLE_VALUE) {
MSG_Error(MSG_APPL_ERROR_GETTING_ANON_TOKEN,
pHitObj->PAppln()->GetMetabaseKey());
fExecuteOnEnd = FALSE;
}
else {
fImpersonationSet = ImpersonateLoggedOnUser(pHitObj->PAppln()->QueryAppConfig()->AnonToken());
if (fImpersonationSet == FALSE) {
MSG_Error(MSG_APPL_ERROR_IMPERSONATING_ANON_USER,
pHitObj->PAppln()->GetMetabaseKey());
fExecuteOnEnd = FALSE;
}
else {
fImpersonationSet = TRUE;
}
}
}
}
if (fOnEndSession && fExecuteOnEnd) {
pHitObj->SetEventState(eEventSesOnEnd);
hr = CallScriptFunction(*pEngineInfo, L"Session_OnEnd");
// We are failing silently here, since there is no corrective action we could take
}
if (fOnEndAppln && fExecuteOnEnd) {
pHitObj->SetEventState(eEventAppOnEnd);
hr = CallScriptFunction(*pEngineInfo, L"Application_OnEnd");
// We are failing silently here, since there is no corrective action we could take
}
// need to undo impersonation if it was set when executing the OnEnd
// routines. Note that a RevertToSelf is the right thing to do here
// as there should not have been any impersonation on the thread in
// the OnEnd case.
if (fImpersonationSet)
RevertToSelf();
LExit:
// restore the codepage in the hit object if it changed after loading
// and the session's codepage wasn't explicitly set.
if ((loadedCodePage != savedCodePage)
&& (!pHitObj->FHasSession() || !pHitObj->PSession()->FCodePageSet()))
pHitObj->SetCodePage(savedCodePage);
if (fUnHideRequestAndResponse)
{
pHitObj->UnHideRequestAndResponseIntrinsics();
}
if (FAILED(hr) && (hr != E_SOURCE_FILE_IS_EMPTY) && pHitObj->FStartApplication() && !fApplnStarted)
{
pHitObj->ApplnOnStartFailed();
}
pHitObj->SetEventState(eEventNone);
// Uninit the scripting namespace
(VOID)intrinsics.PScriptingNamespace()->UnInit();
// Release the template
if (pTemplate)
{
// bug 975: if no script engines, do not do this
if(pTemplate->CountScriptEngines() > 0)
// Return the engine(s) to cache
DeAllocAndFreeEngines(pEngineInfo, pHitObj->PAppln());
// If debugging, notify debugger ONPAGEDONE
if (fDebuggerNotifiedOnStart)
{
Assert(pHitObj->PAppln()->FDebuggable());
pTemplate->NotifyDebuggerOnPageEvent(FALSE);
}
pTemplate->Release();
}
if (fServiceDomainEnterred)
CoLeaveServiceDomain(NULL);
// It is OK if the event function was not found in global.asa
if (hr == DISP_E_UNKNOWNNAME || hr == DISP_E_MEMBERNOTFOUND)
{
hr = S_OK;
}
if (pPoppedASPObjectContext)
pHitObj->SetASPObjectContext(pPoppedASPObjectContext);
if (pASPObjectContext)
pASPObjectContext->Release();
return hr;
}
/*===================================================================
CIntrinsicObjects::Prepare
Prepare intrinsics for the request processing
Parameters:
pSession session holding the instrinsics (can be NULL)
Returns:
HRESULT
===================================================================*/
HRESULT CIntrinsicObjects::Prepare
(
CSession *pSession
)
{
HRESULT hr = S_OK;
if (pSession)
{
// get request, response, server from session
if (SUCCEEDED(hr))
{
m_pRequest = pSession->PRequest();
if (m_pRequest)
m_pRequest->AddRef();
else
hr = E_FAIL;
}
if (SUCCEEDED(hr))
{
m_pResponse = pSession->PResponse();
if (m_pResponse)
m_pResponse->AddRef();
else
hr = E_FAIL;
}
if (SUCCEEDED(hr))
{
m_pServer = pSession->PServer();
if (m_pServer)
m_pServer->AddRef();
else
hr = E_FAIL;
}
}
else
{
// create new request, response, server
if (SUCCEEDED(hr))
{
m_pRequest = new CRequest;
if (!m_pRequest)
hr = E_OUTOFMEMORY;
}
if (SUCCEEDED(hr))
{
m_pResponse = new CResponse;
if (!m_pResponse)
hr = E_OUTOFMEMORY;
}
if (SUCCEEDED(hr))
{
m_pServer = new CServer;
if (!m_pServer)
hr = E_OUTOFMEMORY;
}
}
// init request, response, server
if (SUCCEEDED(hr))
{
Assert(m_pRequest);
hr = m_pRequest->Init();
}
if (SUCCEEDED(hr))
{
Assert(m_pResponse);
hr = m_pResponse->Init();
}
if (SUCCEEDED(hr))
{
Assert(m_pServer);
hr = m_pServer->Init();
}
// create the scripting namespace
if (SUCCEEDED(hr))
{
m_pScriptingNamespace = new CScriptingNamespace;
if (!m_pScriptingNamespace)
hr = E_OUTOFMEMORY;
}
// cleanup on error
if (FAILED(hr))
Cleanup();
m_fIsChild = FALSE;
return hr;
}
/*===================================================================
CIntrinsicObjects::PrepareChild
Prepare intrinsics structure for a child request
Parameters:
pResponse parent intrinsic
pRequest parent intrinsic
pServer parent intrinsic
Returns:
HRESULT
===================================================================*/
HRESULT CIntrinsicObjects::PrepareChild
(
CResponse *pResponse,
CRequest *pRequest,
CServer *pServer
)
{
HRESULT hr = S_OK;
if (!pResponse || !pRequest || !pServer)
{
hr = E_FAIL;
}
if (SUCCEEDED(hr))
{
m_pResponse = pResponse;
m_pResponse->AddRef();
m_pRequest = pRequest;
m_pRequest->AddRef();
m_pServer = pServer;
m_pServer->AddRef();
m_fIsChild = TRUE;
}
if (SUCCEEDED(hr))
{
m_pScriptingNamespace = new CScriptingNamespace;
if (!m_pScriptingNamespace)
hr = E_OUTOFMEMORY;
}
if (FAILED(hr))
Cleanup();
return hr;
}
/*===================================================================
CIntrinsicObjects::Cleanup
Cleanup the intrinsics after the request processing
Parameters:
Returns:
S_OK
===================================================================*/
HRESULT CIntrinsicObjects::Cleanup()
{
if (m_pRequest)
{
if (!m_fIsChild)
m_pRequest->UnInit();
m_pRequest->Release();
m_pRequest = NULL;
}
if (m_pResponse)
{
if (!m_fIsChild)
m_pResponse->UnInit();
m_pResponse->Release();
m_pResponse = NULL;
}
if (m_pServer)
{
if (!m_fIsChild)
m_pServer->UnInit();
m_pServer->Release();
m_pServer = NULL;
}
if (m_pScriptingNamespace)
{
m_pScriptingNamespace->Release();
m_pScriptingNamespace = NULL;
}
return S_OK;
}
/*===================================================================
ReInitIntrinsics
Call re-init on each of the intrinsics that require it
to run a new page.
Parameters:
pHitObj - pointer to the hit object
intrinsics - pointers to the intrinsic IUnknown pointers.
pEngineInfo - some engine info
fPostGlobal - Is this a reinit after running global.asa?
Returns:
S_OK on success
===================================================================*/
HRESULT ReInitIntrinsics
(
CHitObj *pHitObj,
const CIntrinsicObjects &intrinsics,
ActiveEngineInfo *pEngineInfo,
BOOL fPostGlobal
)
{
HRESULT hr;
Assert(pHitObj != NULL);
pHitObj->AssertValid();
Assert(pEngineInfo != NULL);
// Hand the new CIsapiReqInfo to the Server object
// Note on bug 682: We do always need to re-init CServer because it takes the phitobj
if (FAILED(hr = intrinsics.PServer()->ReInit(pHitObj->PIReq(), pHitObj)))
goto LExit;
if (FAILED(hr = intrinsics.PScriptingNamespace()->Init()))
goto LExit;
/*
* Bug 682 & 671 (better fix to 452 & 512)
* Dont re-init the Request & response objects after running a Global.Asa
* because, the running of global.asa may have set cookies into request (bug 671), that reinit
* would wipe, and the global.asa may have output headers (or other stuff) which impacts the response
* object (bug 512) that we dont want to reset.
*/
if (!fPostGlobal)
{
if (FAILED(hr = intrinsics.PRequest()->ReInit(pHitObj->PIReq(), pHitObj)))
goto LExit;
if (FAILED(hr = intrinsics.PResponse()->ReInit(
pHitObj->PIReq(),
pHitObj->PSzNewSessionCookie(),
intrinsics.PRequest(),
GetScriptEngine,
pEngineInfo,
pHitObj
)))
goto LExit;
}
LExit:
return(hr);
}
/*===================================================================
LoadTemplate
Load a template, cleanup and give appropriate errors on failure.
Parameters:
szFile - the file to load a template for
pHitObj - pointer to the hit object
ppTemplate - The returned loaded template
fGlobalAsa - is this for Global.asa?
Returns:
S_OK on success
===================================================================*/
HRESULT LoadTemplate
(
const TCHAR *szFile,
CHitObj *pHitObj,
CTemplate **ppTemplate,
const CIntrinsicObjects &intrinsics,
BOOL fGlobalAsa,
BOOL *pfTemplateInCache)
{
HRESULT hr;
DWORD nRetryCount=0;
Assert(pHitObj != NULL);
pHitObj->AssertValid();
Assert(ppTemplate != NULL);
//
// Verify that we are recieving a valid filename. If not there is something seriously broken.
// We cannot have a browser request without a URL?
//
Assert(szFile);
if (_tcslen(szFile) == 0)
{
// Could not obtain the PathTranslated corresponding to this file.
// Report a Server 500 Error
pHitObj->ReportServerError(IDE_500_SERVER_ERROR);
return E_FAIL;
}
Retry:
// Load the script - cache will AddRef
if (FAILED(hr = g_TemplateCache.Load(
fGlobalAsa,
szFile,
pHitObj->DWInstanceID(),
pHitObj,
ppTemplate,
pfTemplateInCache)))
{
// handle the inpage I/O Error.
// if inpage I/O error is returned, retry the Load up to 5 times...
if (hr == STATUS_IN_PAGE_ERROR /*0xc0000006*/) {
if (nRetryCount++ < 5) {
if (*ppTemplate) {
(*ppTemplate)->Release();
*ppTemplate = NULL;
}
goto Retry;
}
else {
// if retried more than 5 times, give up and return error
hr = E_COULDNT_OPEN_SOURCE_FILE;
}
}
// CONSIDER moving this cleanup into Template.Load
if (hr == E_COULDNT_OPEN_SOURCE_FILE)
{
// Load error string from string table
// BUG 731: added if to retrieve the correct header
WCHAR szwErr[128];
CwchLoadStringOfId(IDH_404_OBJECT_NOT_FOUND, szwErr, 128);
intrinsics.PResponse()->put_Status( szwErr );
HandleSysError(404, 0, IDE_404_OBJECT_NOT_FOUND, NULL, NULL, pHitObj);
#ifndef PERF_DISABLE
g_PerfData.Incr_REQNOTFOUND();
#endif
}
// fix for bug 371
if (*ppTemplate)
{
(*ppTemplate)->Release();
*ppTemplate = NULL;
}
if (hr == E_OUTOFMEMORY)
{
DBGPRINTF((DBG_CONTEXT, "Loading template returned E_OUTOFMEMORY. Flushing template & Script Cache.\n"));
g_TemplateCache.FlushAll();
g_ScriptManager.FlushAll();
}
}
return(hr);
}
/*===================================================================
AllocAndLoadEngines
Allocate and load all the engines we need
Parameters:
pHitObj - The hit object
pTemplate - The template we're gonna run
pEngineInfo - Engine info to fill in
pScriptingNamespace - scripting namespace
fGlobalAsa - Are we loading engines to run global.asa?
Returns:
S_OK on success
===================================================================*/
HRESULT AllocAndLoadEngines
(
CHitObj *pHitObj,
CTemplate *pTemplate,
ActiveEngineInfo *pEngineInfo,
CScriptingNamespace *pScriptingNamespace,
BOOL fGlobalAsa
)
{
HRESULT hr = S_OK;
int iObj;
WORD iEng;
WORD iScriptBlock;
WORD cEngines = pTemplate->CountScriptEngines();
Assert(pHitObj != NULL);
pHitObj->AssertValid();
Assert(pTemplate != NULL);
Assert(pEngineInfo != NULL);
Assert(pScriptingNamespace != NULL);
// Load objects from template into hit object
for (iObj = pTemplate->Count(tcompObjectInfo) - 1; iObj >= 0; --iObj)
{
CHAR *szObjectName = NULL;
CLSID clsid;
CompScope scope;
CompModel model;
CMBCSToWChar convStr;
// get object-info from template and add to hitobj's list of objects
hr = pTemplate->GetObjectInfo(iObj, &szObjectName, &clsid, &scope, &model);
if(FAILED(hr))
goto LExit;
hr = convStr.Init(szObjectName);
if (FAILED(hr))
goto LExit;
// ignore error ?
pHitObj->AddComponent(ctTagged, clsid, scope, model, convStr.GetString());
}
// bug 975: if no script engines, exit now
if(cEngines == 0)
goto LExit;
// Allocate space for script engines
//
// NOTE: There is a timing problem here in that the response object needs to
// be instantiated before we instantiate the script engines, but the
// response object needs to be able to access the list of active script
// engines, because it may need to halt execution. To accomplish this,
// the response object is passed a pointer to the "EngineInfo" structure
// as a pointer, and then we modify the contents of the pointer right under
// its nose. We pass an accessor function via pointer so that response just
// sees a void pointer.
//
if (cEngines == 1)
{
// don't do allocations in case of one engine
pEngineInfo->rgActiveEngines = & (pEngineInfo->siOneActiveEngine);
}
else
{
pEngineInfo->rgActiveEngines = new ScriptingInfo[cEngines];
if (pEngineInfo->rgActiveEngines == NULL)
{
hr = E_OUTOFMEMORY;
goto LExit;
}
}
pEngineInfo->cEngines = cEngines;
pEngineInfo->cActiveEngines = 0; // number of SUCCESSFULLY instantiated engines
// Load all of the script engines in advance.
for (iScriptBlock = 0; iScriptBlock < cEngines; ++iScriptBlock)
{
LPCOLESTR wstrScript;
SCRIPTSTATE nScriptState;
ScriptingInfo *pScriptInfo = &pEngineInfo->rgActiveEngines[iScriptBlock];
pTemplate->GetScriptBlock(
iScriptBlock,
&pScriptInfo->szScriptEngine,
&pScriptInfo->pProgLangId,
&wstrScript);
// Populate information required for the line mapping callback.
//
pScriptInfo->LineMapInfo.iScriptBlock = iScriptBlock;
pScriptInfo->LineMapInfo.pTemplate = pTemplate;
// acquire a script engine by:
//
// getting an engine from the template object (if it has one)
// else from the script manager.
//
// If we are in debug mode, the templates tend to be greedy and hold
// onto script engines. (See notes in scrptmgr.h)
//
pScriptInfo->pScriptEngine = NULL;
if (pHitObj->PAppln()->FDebuggable())
{
pScriptInfo->pScriptEngine = pTemplate->GetActiveScript(iScriptBlock);
if (pScriptInfo->pScriptEngine)
{
// If we got one, we don't need to re-init the engine
nScriptState = SCRIPTSTATE_INITIALIZED;
hr = static_cast<CActiveScriptEngine *>(pScriptInfo->pScriptEngine)->ReuseEngine(pHitObj, NULL, iScriptBlock, pHitObj->DWInstanceID());
}
}
if (pScriptInfo->pScriptEngine == NULL)
{
hr = g_ScriptManager.GetEngine(LOCALE_SYSTEM_DEFAULT,
*(pScriptInfo->pProgLangId),
pTemplate->GetSourceFileName(),
pHitObj,
&pScriptInfo->pScriptEngine,
&nScriptState,
pTemplate,
iScriptBlock);
}
if (FAILED(hr))
goto LExit;
// BUG 252: Keep track of how many engines we actually instantiate
++pEngineInfo->cActiveEngines;
if (nScriptState == SCRIPTSTATE_UNINITIALIZED || fGlobalAsa)
{
if (FAILED(hr = pScriptInfo->pScriptEngine->AddObjects(!fGlobalAsa)))
goto LExit;
}
if (nScriptState == SCRIPTSTATE_UNINITIALIZED)
{
if (FAILED(hr = pScriptInfo->pScriptEngine->AddScriptlet(wstrScript)))
goto LExit;
}
// Add the engine to the scripting namespace
if (FAILED(hr = pScriptingNamespace->AddEngineToNamespace(
(CActiveScriptEngine *)pScriptInfo->pScriptEngine)))
goto LExit;
// Update locale & code page (in case they are different om this page)
pScriptInfo->pScriptEngine->UpdateLocaleInfo(hostinfoLocale);
pScriptInfo->pScriptEngine->UpdateLocaleInfo(hostinfoCodePage);
}
// Add the scripting namespace to each script engine. Because all engines might not
// implement "lazy instantiation", this code requires all
// engines are pre-instantiated (which means we can't do it in the above loop.)
// Add the scripting namespace to the hitobj first
pHitObj->AddScriptingNamespace(pScriptingNamespace);
for (iEng = 0; iEng < pEngineInfo->cActiveEngines; ++iEng)
pEngineInfo->rgActiveEngines[iEng].pScriptEngine->AddScriptingNamespace();
/*
* Bring all engines, except the "primary engine" (engine 0) when the template
* isn't global.asa, to runnable state in the order in which script for the
* given language was found in the script file.
*/
for (iEng = fGlobalAsa ? 0 : 1; iEng < pEngineInfo->cActiveEngines; ++iEng)
{
hr = pEngineInfo->rgActiveEngines[iEng].pScriptEngine->MakeEngineRunnable();
if (FAILED(hr))
goto LExit;
}
LExit:
return(hr);
}
/*===================================================================
DeAllocAndFreeEngines
Deallocate and free any loaded engines
Parameters:
pEngineInfo - Engine info to release
Returns:
Nothing
===================================================================*/
VOID DeAllocAndFreeEngines
(
ActiveEngineInfo *pEngineInfo,
CAppln *pAppln
)
{
WORD iEng;
Assert(pEngineInfo != NULL);
if (pEngineInfo->cActiveEngines > 0) {
if (pEngineInfo->rgActiveEngines == NULL) {
Assert(pEngineInfo->rgActiveEngines);
}
else {
for (iEng = 0; iEng < pEngineInfo->cActiveEngines; ++iEng)
g_ScriptManager.ReturnEngineToCache(&pEngineInfo->rgActiveEngines[iEng].pScriptEngine, pAppln);
pEngineInfo->cActiveEngines = 0;
}
}
if (pEngineInfo->cEngines > 1)
{
delete pEngineInfo->rgActiveEngines;
}
pEngineInfo->cEngines = 0;
pEngineInfo->rgActiveEngines = NULL;
}
/*===================================================================
GetScriptEngine
Get a script engine based on index. Return NULL if the index
is not in range. (this is a callback)
The AllocAndLoadEngines function will create an array of ScriptingInfo
structures that are defined here. It contains all the infomation
needed to 1. set up the MapScript2SourceLine callback,
2. merge namespaces, 3. set up this callback.
Parameters:
iScriptEngine - the script engine to retrieve
pvData - instance data for the function
Returns:
The requested script engine or NULL if not such engine
Side effects:
None
===================================================================*/
CScriptEngine *GetScriptEngine
(
INT iScriptEngine,
VOID *pvData
)
{
ActiveEngineInfo *pInfo = static_cast<ActiveEngineInfo *>(pvData);
if (unsigned(iScriptEngine) >= unsigned(pInfo->cActiveEngines))
{
// Note: the caller has no idea how many script engines there are.
// if the caller asks for an engine out of range, return NULL so they
// know they have asked for more than there are
return NULL;
}
return(pInfo->rgActiveEngines[iScriptEngine].pScriptEngine);
}
/*===================================================================
CallScriptFunctionOfEngine
Calls a script engine to execute one of its functions
Returns:
S_OK on success
Side effects:
None
===================================================================*/
HRESULT CallScriptFunctionOfEngine
(
ActiveEngineInfo &engineInfo,
short iScriptBlock,
wchar_t *strFunction,
CASPObjectContext *pASPObjectContext /* = NULL */
)
{
HRESULT hr;
Assert(engineInfo.rgActiveEngines != NULL);
Assert (iScriptBlock <= engineInfo.cEngines);
CScriptEngine *pScriptEngine = (CScriptEngine *)engineInfo.rgActiveEngines[iScriptBlock].pScriptEngine;
Assert(pScriptEngine != NULL);
hr = pScriptEngine->Call(strFunction);
// housekeeping for the transacted case...
if (pASPObjectContext != NULL) {
// If the script timed out or there was an unhandled error, then autoabort
if (SUCCEEDED(hr) && (pScriptEngine->FScriptTimedOut() || pScriptEngine->FScriptHadError()))
{
hr = pASPObjectContext->SetAbort();
}
// If the script author did not do an explicit SetComplete or SetAbort
// then do a SetComplete here so Viper will return the transaction
// completion status to the caller
if (SUCCEEDED(hr) && !pASPObjectContext->FAborted())
{
hr = pASPObjectContext->SetComplete();
}
}
return hr;
}
/*===================================================================
CallScriptFunction
Calls each script engine in turn to execute a script function;
exits when an engine succeeds or we run out of engines.
Returns:
S_OK on success
Side effects:
None
===================================================================*/
HRESULT CallScriptFunction
(
ActiveEngineInfo &engineInfo,
wchar_t *strFunction
)
{
HRESULT hr = E_FAIL;
int i;
for (i = 0; i < engineInfo.cActiveEngines; i++)
{
// if execution succeeds, bail
if (SUCCEEDED(hr = CallScriptFunctionOfEngine(engineInfo, (SHORT)i, strFunction)))
goto LExit;
// if execution fails with exception other then unknown name, bail
if (hr != DISP_E_UNKNOWNNAME && hr != DISP_E_MEMBERNOTFOUND)
goto LExit;
}
LExit:
return hr;
}
/*===================================================================
TestScriptFunction
Tests each script engine in turn to test [the existance of] a script
function; exits when an engine succeeds or we run out of engines.
Parameters
ActiveEngineInfo &engineInfo
wchar_t *strFunction functions name
Returns:
S_OK if exists
Side effects:
None
===================================================================*/
HRESULT TestScriptFunction
(
ActiveEngineInfo &engineInfo,
wchar_t *strFunction
)
{
HRESULT hr = E_FAIL;
for (int i = 0; i < engineInfo.cActiveEngines; i++)
{
hr = engineInfo.rgActiveEngines[i].pScriptEngine->
CheckEntryPoint(strFunction);
// if execution succeeds, bail
if (SUCCEEDED(hr))
break;
// if fails with result other then unknown name, bail
if (hr != DISP_E_UNKNOWNNAME && hr != DISP_E_MEMBERNOTFOUND)
break;
}
return hr;
}