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.
2486 lines
59 KiB
2486 lines
59 KiB
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name :
|
|
isapi_handler.cxx
|
|
|
|
Abstract:
|
|
Handle ISAPI extension requests
|
|
|
|
Author:
|
|
Taylor Weiss (TaylorW) 27-Jan-2000
|
|
|
|
Environment:
|
|
Win32 - User Mode
|
|
|
|
Project:
|
|
ULW3.DLL
|
|
|
|
--*/
|
|
|
|
#include <initguid.h>
|
|
#include "precomp.hxx"
|
|
#include "isapi_handler.h"
|
|
#include <wmrgexp.h>
|
|
#include <iisextp.h>
|
|
#include <errno.h>
|
|
|
|
HMODULE W3_ISAPI_HANDLER::sm_hIsapiModule;
|
|
PFN_ISAPI_TERM_MODULE W3_ISAPI_HANDLER::sm_pfnTermIsapiModule;
|
|
PFN_ISAPI_PROCESS_REQUEST W3_ISAPI_HANDLER::sm_pfnProcessIsapiRequest;
|
|
PFN_ISAPI_PROCESS_COMPLETION W3_ISAPI_HANDLER::sm_pfnProcessIsapiCompletion;
|
|
W3_INPROC_ISAPI_HASH * W3_ISAPI_HANDLER::sm_pInprocIsapiHash;
|
|
CRITICAL_SECTION W3_ISAPI_HANDLER::sm_csInprocHashLock;
|
|
CRITICAL_SECTION W3_ISAPI_HANDLER::sm_csBigHurkinWamRegLock;
|
|
WAM_PROCESS_MANAGER * W3_ISAPI_HANDLER::sm_pWamProcessManager;
|
|
BOOL W3_ISAPI_HANDLER::sm_fWamActive;
|
|
CHAR W3_ISAPI_HANDLER::sm_szInstanceId[SIZE_CLSID_STRING];
|
|
|
|
BOOL sg_Initialized = FALSE;
|
|
|
|
/***********************************************************************
|
|
Local Declarations
|
|
***********************************************************************/
|
|
|
|
VOID
|
|
AddFiltersToMultiSz(
|
|
IN const MB & mb,
|
|
IN LPCWSTR szFilterPath,
|
|
IN OUT MULTISZ * pmsz
|
|
);
|
|
|
|
VOID
|
|
AddAllFiltersToMultiSz(
|
|
IN const MB & mb,
|
|
IN OUT MULTISZ * pmsz
|
|
);
|
|
|
|
/***********************************************************************
|
|
Module Definitions
|
|
***********************************************************************/
|
|
|
|
CONTEXT_STATUS
|
|
W3_ISAPI_HANDLER::DoWork(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Main ISAPI handler routine
|
|
|
|
Return Value:
|
|
|
|
CONTEXT_STATUS_PENDING if async pending,
|
|
else CONTEXT_STATUS_CONTINUE
|
|
|
|
--*/
|
|
{
|
|
W3_CONTEXT *pW3Context = QueryW3Context();
|
|
DBG_ASSERT( pW3Context != NULL );
|
|
|
|
HRESULT hr;
|
|
BOOL fComplete = FALSE;
|
|
W3_REQUEST *pRequest = pW3Context->QueryRequest();
|
|
W3_METADATA *pMetaData = pW3Context->QueryUrlContext()->QueryMetaData();
|
|
|
|
//
|
|
// Preload entity if needed
|
|
//
|
|
|
|
hr = pRequest->PreloadEntityBody( pW3Context,
|
|
&fComplete );
|
|
//
|
|
// If we cannot read the request entity, we will assume it is the
|
|
// client's fault
|
|
//
|
|
if ( FAILED( hr ) )
|
|
{
|
|
if ( hr == HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY ) )
|
|
{
|
|
pW3Context->SetErrorStatus( hr );
|
|
|
|
pW3Context->QueryResponse()->SetStatus( HttpStatusServerError );
|
|
}
|
|
else if ( hr == HRESULT_FROM_WIN32( ERROR_CONNECTION_INVALID ) )
|
|
{
|
|
pW3Context->QueryResponse()->SetStatus( HttpStatusEntityTooLarge );
|
|
}
|
|
else
|
|
{
|
|
pW3Context->SetErrorStatus( hr );
|
|
|
|
pW3Context->QueryResponse()->SetStatus( HttpStatusBadRequest );
|
|
}
|
|
|
|
return CONTEXT_STATUS_CONTINUE;
|
|
}
|
|
|
|
if ( !fComplete )
|
|
{
|
|
//
|
|
// Async read pending. Just bail
|
|
//
|
|
|
|
return CONTEXT_STATUS_PENDING;
|
|
}
|
|
|
|
//
|
|
// If we've already exceeded the maximum allowed entity, we
|
|
// need to fail.
|
|
//
|
|
|
|
if ( pW3Context->QueryMainContext()->QueryEntityReadSoFar() >
|
|
pMetaData->QueryMaxRequestEntityAllowed() )
|
|
{
|
|
pW3Context->QueryResponse()->SetStatus( HttpStatusEntityTooLarge );
|
|
|
|
return CONTEXT_STATUS_CONTINUE;
|
|
}
|
|
|
|
_fEntityBodyPreloadComplete = TRUE;
|
|
_State = ISAPI_STATE_INITIALIZING;
|
|
|
|
return IsapiDoWork( pW3Context );
|
|
}
|
|
|
|
CONTEXT_STATUS
|
|
W3_ISAPI_HANDLER::IsapiDoWork(
|
|
W3_CONTEXT * pW3Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called to execute an ISAPI. This routine must be called only after
|
|
we have preloaded entity for the request
|
|
|
|
Arguments:
|
|
|
|
pW3Context - Context for this request
|
|
|
|
Return Value:
|
|
|
|
CONTEXT_STATUS_PENDING if async pending,
|
|
else CONTEXT_STATUS_CONTINUE
|
|
|
|
--*/
|
|
{
|
|
DWORD dwHseResult;
|
|
HRESULT hr = NOERROR;
|
|
HANDLE hOopToken;
|
|
URL_CONTEXT * pUrlContext;
|
|
BOOL fIsVrToken;
|
|
DWORD dwWamSubError = 0;
|
|
HTTP_SUB_ERROR httpSubError;
|
|
|
|
//
|
|
// We must have preloaded entity by the time this is called
|
|
//
|
|
|
|
DBG_ASSERT( _State == ISAPI_STATE_INITIALIZING );
|
|
DBG_ASSERT( _fEntityBodyPreloadComplete );
|
|
DBG_ASSERT( sm_pfnProcessIsapiRequest );
|
|
DBG_ASSERT( sm_pfnProcessIsapiCompletion );
|
|
|
|
DBG_REQUIRE( ( pUrlContext = pW3Context->QueryUrlContext() ) != NULL );
|
|
|
|
IF_DEBUG( ISAPI )
|
|
{
|
|
DBGPRINTF((
|
|
DBG_CONTEXT,
|
|
"IsapiDoWork called for new request.\r\n"
|
|
));
|
|
}
|
|
|
|
//
|
|
// Initialize the ISAPI_CORE_DATA and ISAPI_CORE_INTERFACE
|
|
// for this request
|
|
//
|
|
|
|
if ( FAILED( hr = InitCoreData( &fIsVrToken ) ) )
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
DBG_ASSERT( _pCoreData );
|
|
|
|
//
|
|
// If the gateway image is not enabled, then we should fail the
|
|
// request with a 404.
|
|
//
|
|
|
|
if ( g_pW3Server->QueryIsIsapiImageEnabled( _pCoreData->szGatewayImage ) == FALSE )
|
|
{
|
|
_State = ISAPI_STATE_FAILED;
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"ISAPI image disabled: %S.\r\n",
|
|
_pCoreData->szGatewayImage ));
|
|
|
|
pW3Context->SetErrorStatus( ERROR_ACCESS_DISABLED_BY_POLICY );
|
|
|
|
pW3Context->QueryResponse()->SetStatus( HttpStatusNotFound,
|
|
Http404DeniedByPolicy );
|
|
|
|
hr = pW3Context->SendResponse( W3_FLAG_ASYNC );
|
|
|
|
if ( SUCCEEDED( hr ) )
|
|
{
|
|
return CONTEXT_STATUS_PENDING;
|
|
}
|
|
else
|
|
{
|
|
return CONTEXT_STATUS_CONTINUE;
|
|
}
|
|
}
|
|
|
|
_pIsapiRequest = new (pW3Context) ISAPI_REQUEST( pW3Context, _pCoreData->fIsOop );
|
|
|
|
if ( _pIsapiRequest == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if ( FAILED( hr = _pIsapiRequest->Create() ) )
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// If the request should run OOP, get the WAM process and
|
|
// duplicate the impersonation token
|
|
//
|
|
|
|
if ( _pCoreData->fIsOop )
|
|
{
|
|
DBG_ASSERT( sm_pWamProcessManager );
|
|
DBG_ASSERT( _pCoreData->szWamClsid[0] != L'\0' );
|
|
|
|
hr = sm_pWamProcessManager->GetWamProcess(
|
|
(LPCWSTR)&_pCoreData->szWamClsid,
|
|
_pCoreData->szApplMdPathW,
|
|
&dwWamSubError,
|
|
&_pWamProcess,
|
|
sm_szInstanceId
|
|
);
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// We have to modify the token that we're passing to
|
|
// the OOP process.
|
|
//
|
|
|
|
if( ( fIsVrToken && !pW3Context->QueryVrToken()->QueryOOPToken() ) ||
|
|
( !fIsVrToken && !pW3Context->QueryUserContext()->QueryIsCachedToken() ) ||
|
|
( !fIsVrToken && !pW3Context->QueryUserContext()->QueryCachedToken()->QueryOOPToken() ) )
|
|
{
|
|
hr = GrantWpgAccessToToken( _pCoreData->hToken );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
hr = AddWpgToTokenDefaultDacl( _pCoreData->hToken );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
if ( DuplicateHandle( GetCurrentProcess(), _pCoreData->hToken,
|
|
_pWamProcess->QueryProcess(), &hOopToken,
|
|
0, FALSE, DUPLICATE_SAME_ACCESS ) )
|
|
{
|
|
_pCoreData->hToken = hOopToken;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
|
|
//
|
|
// CODEWORK - If the target process has exited, then
|
|
// DuplicateHandle fails with ERROR_ACCESS_DENIED. This
|
|
// will confuse our error handling logic because it'll
|
|
// thing that we really got this error attempting to
|
|
// process the request.
|
|
//
|
|
// For the time being, we'll detect this error and let
|
|
// it call into ProcessRequest. If the process really
|
|
// has exited, this will cause the WAM_PROCESS cleanup
|
|
// code to recover everything.
|
|
//
|
|
// In the future, we should consider waiting on the
|
|
// process handle to detect steady-state crashes of
|
|
// OOP hosts so that we don't have to discover the
|
|
// problem only when something trys to talk to the
|
|
// process.
|
|
//
|
|
// Another thing to consider is that we could trigger
|
|
// the crash recovery directly and call GetWamProcess
|
|
// again to get a new process. This would make the
|
|
// crash completely transparent to the client, which
|
|
// would be an improvement over the current solution
|
|
// (and IIS 4 and 5) which usually waits until a client
|
|
// request fails before recovering.
|
|
//
|
|
|
|
_pCoreData->hToken = NULL;
|
|
|
|
if ( hr != HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED ) )
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Handle the request
|
|
//
|
|
|
|
_State = ISAPI_STATE_PENDING;
|
|
|
|
//
|
|
// Temporarily up the thread threshold since we don't know when the
|
|
// ISAPI will return
|
|
//
|
|
|
|
ThreadPoolSetInfo( ThreadPoolIncMaxPoolThreads, 0 );
|
|
|
|
if ( !_pCoreData->fIsOop )
|
|
{
|
|
IF_DEBUG( ISAPI )
|
|
{
|
|
DBGPRINTF((
|
|
DBG_CONTEXT,
|
|
"Processing ISAPI_REQUEST %p OOP.\r\n",
|
|
_pIsapiRequest
|
|
));
|
|
}
|
|
|
|
hr = sm_pfnProcessIsapiRequest(
|
|
_pIsapiRequest,
|
|
_pCoreData,
|
|
&dwHseResult
|
|
);
|
|
}
|
|
else
|
|
{
|
|
hr = _pWamProcess->ProcessRequest(
|
|
_pIsapiRequest,
|
|
_pCoreData,
|
|
&dwHseResult
|
|
);
|
|
}
|
|
|
|
//
|
|
// Back down the count since the ISAPI has returned
|
|
//
|
|
|
|
ThreadPoolSetInfo( ThreadPoolDecMaxPoolThreads, 0 );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
_State = ISAPI_STATE_FAILED;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Determine whether the extension was synchronous or pending.
|
|
// We need to do this before releasing our reference on the
|
|
// ISAPI_REQUEST since the final release will check the state
|
|
// to determine if an additional completion is necessary.
|
|
//
|
|
// In either case, we should just return with the appropriate
|
|
// return code after setting the state.
|
|
//
|
|
|
|
if ( dwHseResult != HSE_STATUS_PENDING &&
|
|
_pCoreData->fIsOop == FALSE )
|
|
{
|
|
_State = ISAPI_STATE_DONE;
|
|
|
|
//
|
|
// This had better be the final release...
|
|
//
|
|
|
|
LONG Refs = _pIsapiRequest->Release();
|
|
|
|
//
|
|
// Make /W3 happy on a free build...
|
|
//
|
|
|
|
if ( Refs != 0 )
|
|
{
|
|
DBG_ASSERT( Refs == 0 );
|
|
}
|
|
|
|
return CONTEXT_STATUS_CONTINUE;
|
|
}
|
|
|
|
//
|
|
// This may or may not be the final release...
|
|
//
|
|
_pIsapiRequest->Release();
|
|
|
|
return CONTEXT_STATUS_PENDING;
|
|
|
|
ErrorExit:
|
|
|
|
DBG_ASSERT( FAILED( hr ) );
|
|
|
|
//
|
|
// Spew on failure.
|
|
//
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
DBGPRINTF((
|
|
DBG_CONTEXT,
|
|
"Attempt to process ISAPI request failed. Error 0x%08x.\r\n",
|
|
hr
|
|
));
|
|
}
|
|
|
|
//
|
|
// Set the error status now.
|
|
//
|
|
|
|
pW3Context->SetErrorStatus( hr );
|
|
|
|
//
|
|
// If we've failed, and the state is ISAPI_STATE_INITIALIZING, then
|
|
// we never made the call out to the extension. It's therefore
|
|
// safe to handle the error and advance the state machine.
|
|
//
|
|
// It's also safe to advance the state machine in the special case
|
|
// error where the attempt to call the extension results in
|
|
// ERROR_ACCESS_DENIED.
|
|
//
|
|
|
|
if ( _State == ISAPI_STATE_INITIALIZING ||
|
|
hr == HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED) )
|
|
{
|
|
//
|
|
// Setting the state to ISAPI_STATE_DONE will cause the
|
|
// next completion to advance the state machine.
|
|
//
|
|
|
|
_State = ISAPI_STATE_DONE;
|
|
|
|
//
|
|
// The _pWamProcess and _pCoreData members are cleaned up
|
|
// by the destructor. We don't need to worry about them.
|
|
// We also don't need to worry about any tokens that we
|
|
// are using. The tokens local to this process are owned
|
|
// by W3_USER_CONTEXT; any duplicates for OOP are the
|
|
// responsibility of the dllhost process (if it still
|
|
// exists).
|
|
//
|
|
// We do need to clean up the _pIsapiRequest if we've
|
|
// created it. This had better well be the final release.
|
|
//
|
|
|
|
if ( _pIsapiRequest )
|
|
{
|
|
_pIsapiRequest->Release();
|
|
}
|
|
|
|
//
|
|
// Set the HTTP status and send it asynchronously.
|
|
// This will ultimately trigger the completion that
|
|
// advances the state machine.
|
|
//
|
|
|
|
if ( hr == HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED ) )
|
|
{
|
|
pW3Context->QueryResponse()->SetStatus( HttpStatusUnauthorized,
|
|
Http401Resource );
|
|
}
|
|
else
|
|
{
|
|
pW3Context->QueryResponse()->SetStatus( HttpStatusServerError );
|
|
|
|
if ( dwWamSubError != 0 )
|
|
{
|
|
httpSubError.dwStringId = dwWamSubError;
|
|
pW3Context->QueryResponse()->SetSubError( &httpSubError );
|
|
}
|
|
}
|
|
|
|
hr = pW3Context->SendResponse( W3_FLAG_ASYNC );
|
|
|
|
if ( SUCCEEDED( hr ) )
|
|
{
|
|
return CONTEXT_STATUS_PENDING;
|
|
}
|
|
|
|
//
|
|
// Ouch - couldn't send the error page...
|
|
//
|
|
|
|
_State = ISAPI_STATE_FAILED;
|
|
|
|
return CONTEXT_STATUS_CONTINUE;
|
|
}
|
|
|
|
//
|
|
// If we get here, then an error has occured during or after
|
|
// our call into the extension. Because it's possible for an
|
|
// OOP call to fail after entering the extension, we can't
|
|
// generally know our state.
|
|
//
|
|
// Because of this, we'll assume that the extension has
|
|
// outstanding references to the ISAPI_REQUEST on its behalf.
|
|
// We'll set the state to ISAPI_STATE_PENDING and let the
|
|
// destructor on the ISAPI_REQUEST trigger the final
|
|
// completion.
|
|
//
|
|
// Also, we'll set the error status, just in case the extension
|
|
// didn't get a response off before it failed.
|
|
//
|
|
|
|
pW3Context->QueryResponse()->SetStatus( HttpStatusServerError );
|
|
|
|
_State = ISAPI_STATE_FAILED;
|
|
|
|
//
|
|
// This may or may not be the final release on the ISAPI_REQUEST.
|
|
//
|
|
// It had better be non-NULL if we got past ISAPI_STATE_INITIALIZING.
|
|
//
|
|
|
|
DBG_ASSERT( _pIsapiRequest );
|
|
|
|
_pIsapiRequest->Release();
|
|
|
|
return CONTEXT_STATUS_PENDING;
|
|
}
|
|
|
|
CONTEXT_STATUS
|
|
W3_ISAPI_HANDLER::OnCompletion(
|
|
DWORD cbCompletion,
|
|
DWORD dwCompletionStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ISAPI async completion handler.
|
|
|
|
Arguments:
|
|
|
|
cbCompletion - Number of bytes in an async completion
|
|
dwCompletionStatus - Error status of a completion
|
|
|
|
Return Value:
|
|
|
|
CONTEXT_STATUS_PENDING if async pending,
|
|
else CONTEXT_STATUS_CONTINUE
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
W3_CONTEXT * pW3Context;
|
|
|
|
pW3Context = QueryW3Context();
|
|
DBG_ASSERT( pW3Context != NULL );
|
|
|
|
//
|
|
// Is this completion for the entity body preload? If so note the
|
|
// number of bytes and start handling the ISAPI request
|
|
//
|
|
|
|
if ( !_fEntityBodyPreloadComplete )
|
|
{
|
|
BOOL fComplete = FALSE;
|
|
|
|
//
|
|
// This completion is for entity body preload
|
|
//
|
|
|
|
W3_REQUEST *pRequest = pW3Context->QueryRequest();
|
|
hr = pRequest->PreloadCompletion(pW3Context,
|
|
cbCompletion,
|
|
dwCompletionStatus,
|
|
&fComplete);
|
|
|
|
//
|
|
// If we cannot read the request entity, we will assume it is the
|
|
// client's fault
|
|
//
|
|
if ( FAILED( hr ) )
|
|
{
|
|
|
|
if ( hr == HRESULT_FROM_WIN32( ERROR_CONNECTION_INVALID ) )
|
|
{
|
|
pW3Context->QueryResponse()->SetStatus( HttpStatusEntityTooLarge );
|
|
}
|
|
else
|
|
{
|
|
pW3Context->SetErrorStatus( hr );
|
|
|
|
pW3Context->QueryResponse()->SetStatus( HttpStatusBadRequest );
|
|
}
|
|
|
|
return CONTEXT_STATUS_CONTINUE;
|
|
}
|
|
|
|
if (!fComplete)
|
|
{
|
|
return CONTEXT_STATUS_PENDING;
|
|
}
|
|
|
|
_fEntityBodyPreloadComplete = TRUE;
|
|
_State = ISAPI_STATE_INITIALIZING;
|
|
|
|
//
|
|
// Finally we can call the ISAPI
|
|
//
|
|
|
|
return IsapiDoWork( pW3Context );
|
|
}
|
|
|
|
DBG_ASSERT( _fEntityBodyPreloadComplete );
|
|
|
|
return IsapiOnCompletion( cbCompletion, dwCompletionStatus );
|
|
}
|
|
|
|
CONTEXT_STATUS
|
|
W3_ISAPI_HANDLER::IsapiOnCompletion(
|
|
DWORD cbCompletion,
|
|
DWORD dwCompletionStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Funnels a completion to an ISAPI
|
|
|
|
Arguments:
|
|
|
|
cbCompletion - Bytes of completion
|
|
dwCompletionStatus - Win32 Error status of completion
|
|
|
|
Return Value:
|
|
|
|
CONTEXT_STATUS_PENDING if async pending,
|
|
else CONTEXT_STATUS_CONTINUE
|
|
|
|
--*/
|
|
{
|
|
DWORD64 IsapiContext;
|
|
HRESULT hr = NO_ERROR;
|
|
|
|
//
|
|
// If the state is ISAPI_STATE_DONE, then we should
|
|
// advance the state machine now.
|
|
//
|
|
|
|
if ( _State == ISAPI_STATE_DONE ||
|
|
_State == ISAPI_STATE_FAILED )
|
|
{
|
|
return CONTEXT_STATUS_CONTINUE;
|
|
}
|
|
|
|
//
|
|
// If we get here, then this completion should be passed
|
|
// along to the extension.
|
|
//
|
|
|
|
DBG_ASSERT( _pCoreData );
|
|
DBG_ASSERT( _pIsapiRequest );
|
|
|
|
IsapiContext = _pIsapiRequest->QueryIsapiContext();
|
|
|
|
DBG_ASSERT( IsapiContext != 0 );
|
|
|
|
//
|
|
// Process the completion.
|
|
//
|
|
|
|
_pIsapiRequest->AddRef();
|
|
|
|
//
|
|
// Temporarily up the thread threshold since we don't know when the
|
|
// ISAPI will return
|
|
//
|
|
|
|
ThreadPoolSetInfo( ThreadPoolIncMaxPoolThreads, 0 );
|
|
|
|
if ( !_pCoreData->fIsOop )
|
|
{
|
|
//
|
|
// Need to reset the ISAPI context in case the extension
|
|
// does another async operation before the below call returns
|
|
//
|
|
|
|
_pIsapiRequest->ResetIsapiContext();
|
|
|
|
hr = sm_pfnProcessIsapiCompletion(
|
|
IsapiContext,
|
|
cbCompletion,
|
|
dwCompletionStatus
|
|
);
|
|
}
|
|
else
|
|
{
|
|
DBG_ASSERT( _pWamProcess );
|
|
|
|
//
|
|
// _pWamProcess->ProcessCompletion depends on the ISAPI
|
|
// context, so it will make the Reset call.
|
|
//
|
|
|
|
hr = _pWamProcess->ProcessCompletion(
|
|
_pIsapiRequest,
|
|
IsapiContext,
|
|
cbCompletion,
|
|
dwCompletionStatus
|
|
);
|
|
}
|
|
|
|
//
|
|
// Back down the count since the ISAPI has returned
|
|
//
|
|
|
|
ThreadPoolSetInfo( ThreadPoolDecMaxPoolThreads, 0 );
|
|
|
|
_pIsapiRequest->Release();
|
|
|
|
return CONTEXT_STATUS_PENDING;
|
|
}
|
|
|
|
HRESULT
|
|
W3_ISAPI_HANDLER::InitCoreData(
|
|
BOOL * pfIsVrToken
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes the ISAPI_CORE_DATA for a request
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = NO_ERROR;
|
|
W3_CONTEXT * pW3Context;
|
|
URL_CONTEXT * pUrlContext;
|
|
W3_URL_INFO * pUrlInfo;
|
|
W3_METADATA * pMetadata;
|
|
W3_REQUEST * pRequest = NULL;
|
|
LPCSTR szContentLength = NULL;
|
|
STRU stru;
|
|
STRU * pstru = NULL;
|
|
BOOL fRequestIsScript = FALSE;
|
|
BOOL fUsePathInfo;
|
|
DWORD dwAppType = APP_INPROC;
|
|
|
|
DBG_REQUIRE( ( pW3Context = QueryW3Context() ) != NULL );
|
|
DBG_REQUIRE( ( pRequest = pW3Context->QueryRequest() ) != NULL );
|
|
DBG_REQUIRE( ( pUrlContext = pW3Context->QueryUrlContext() ) != NULL );
|
|
DBG_REQUIRE( ( pUrlInfo = pUrlContext->QueryUrlInfo() ) != NULL );
|
|
DBG_REQUIRE( ( pMetadata = pUrlContext->QueryMetaData() ) != NULL );
|
|
|
|
//
|
|
// Check for script
|
|
//
|
|
|
|
if ( QueryScriptMapEntry() )
|
|
{
|
|
fRequestIsScript = TRUE;
|
|
}
|
|
|
|
//
|
|
// Populate data directly into the inline core data.
|
|
// Assume everything is inproc. In the case of an
|
|
// OOP request, the SerializeCoreDataForOop function
|
|
// will take care of any needed changes.
|
|
//
|
|
|
|
_pCoreData = &_InlineCoreData;
|
|
|
|
//
|
|
// Structure size information
|
|
//
|
|
|
|
_pCoreData->cbSize = sizeof(ISAPI_CORE_DATA);
|
|
|
|
//
|
|
// WAM information - Always set to inproc for w3wp.exe
|
|
//
|
|
|
|
_pCoreData->szWamClsid[0] = L'\0';
|
|
_pCoreData->fIsOop = FALSE;
|
|
|
|
//
|
|
// Secure request?
|
|
//
|
|
|
|
_pCoreData->fSecure = pRequest->IsSecureRequest();
|
|
|
|
//
|
|
// Client HTTP version
|
|
//
|
|
|
|
_pCoreData->dwVersionMajor = pRequest->QueryVersion().MajorVersion;
|
|
_pCoreData->dwVersionMinor = pRequest->QueryVersion().MinorVersion;
|
|
|
|
//
|
|
// Site instance ID
|
|
//
|
|
|
|
_pCoreData->dwInstanceId = pRequest->QuerySiteId();
|
|
|
|
//
|
|
// Request content-length
|
|
//
|
|
|
|
if ( pRequest->IsChunkedRequest() )
|
|
{
|
|
_pCoreData->dwContentLength = (DWORD) -1;
|
|
}
|
|
else
|
|
{
|
|
szContentLength = pRequest->GetHeader( HttpHeaderContentLength );
|
|
|
|
if ( szContentLength != NULL )
|
|
{
|
|
errno = 0;
|
|
|
|
_pCoreData->dwContentLength = strtoul( szContentLength, NULL, 10 );
|
|
|
|
//
|
|
// Check for overflow
|
|
//
|
|
|
|
if ( ( _pCoreData->dwContentLength == ULONG_MAX ||
|
|
_pCoreData->dwContentLength == 0 ) &&
|
|
errno == ERANGE )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( ERROR_ARITHMETIC_OVERFLOW );
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_pCoreData->dwContentLength = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Client authentication information
|
|
//
|
|
|
|
_pCoreData->hToken = pW3Context->QueryImpersonationToken( pfIsVrToken );
|
|
_pCoreData->pSid = pW3Context->QueryUserContext()->QuerySid();
|
|
|
|
//
|
|
// Request ID
|
|
//
|
|
|
|
_pCoreData->RequestId = pRequest->QueryRequestId();
|
|
|
|
//
|
|
// Gateway image
|
|
//
|
|
// There are 3 cases to consider:
|
|
//
|
|
// 1) If this is a DAV request, then use DAV image
|
|
// 2) If this is script mapped, use script executable
|
|
// 3) Else use URL physical path
|
|
//
|
|
|
|
if ( _fIsDavRequest )
|
|
{
|
|
pstru = &_strDavIsapiImage;
|
|
}
|
|
else if ( fRequestIsScript )
|
|
{
|
|
pstru = QueryScriptMapEntry()->QueryExecutable();
|
|
}
|
|
else
|
|
{
|
|
pstru = pUrlContext->QueryPhysicalPath();
|
|
}
|
|
|
|
DBG_ASSERT( pstru );
|
|
|
|
_pCoreData->szGatewayImage = pstru->QueryStr();
|
|
_pCoreData->cbGatewayImage = pstru->QueryCB() + sizeof(WCHAR);
|
|
|
|
pstru = NULL;
|
|
|
|
//
|
|
// Get the app type.
|
|
//
|
|
|
|
if ( sm_fWamActive )
|
|
{
|
|
dwAppType = pMetadata->QueryAppIsolated();
|
|
|
|
if ( dwAppType == APP_ISOLATED ||
|
|
dwAppType == APP_POOL )
|
|
{
|
|
if ( IsInprocIsapi( _pCoreData->szGatewayImage ) )
|
|
{
|
|
dwAppType = APP_INPROC;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Physical Path - Not Needed? Just set it to an empty string for now.
|
|
//
|
|
|
|
hr = _PhysicalPath.Copy( "" );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
_pCoreData->szPhysicalPath = _PhysicalPath.QueryStr();
|
|
_pCoreData->cbPhysicalPath = _PhysicalPath.QueryCB() + sizeof(CHAR);
|
|
|
|
//
|
|
// Path info
|
|
//
|
|
// 1) If this is a DAV request, PATH_INFO is the URL
|
|
// 2) If this is a script, and AllowPathInfoForScriptMappings is
|
|
// FALSE, then PATH_INFO is the URL.
|
|
// 3) Else, we use the "real" PATH_INFO
|
|
//
|
|
// We will set a flag indicating whether we are using the URL or
|
|
// the "real" data, so that we can be efficient about deriving
|
|
// PATH_TRANSLATED.
|
|
//
|
|
|
|
fUsePathInfo = TRUE;
|
|
|
|
if ( _fIsDavRequest )
|
|
{
|
|
hr = pRequest->GetUrl( &stru );
|
|
|
|
fUsePathInfo = FALSE;
|
|
}
|
|
else if ( fRequestIsScript )
|
|
{
|
|
if ( pW3Context->QuerySite()->QueryAllowPathInfoForScriptMappings() )
|
|
{
|
|
hr = stru.Copy( *pUrlInfo->QueryPathInfo() );
|
|
}
|
|
else
|
|
{
|
|
hr = pRequest->GetUrl( &stru );
|
|
|
|
fUsePathInfo = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = stru.Copy( *pUrlInfo->QueryPathInfo() );
|
|
}
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
hr = _PathInfo.CopyW( stru.QueryStr(),
|
|
stru.QueryCCH() );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
_pCoreData->szPathInfo = _PathInfo.QueryStr();
|
|
_pCoreData->cbPathInfo = _PathInfo.QueryCB() + sizeof(CHAR);
|
|
|
|
//
|
|
// Method
|
|
//
|
|
|
|
hr = pRequest->GetVerbString( &_Method );
|
|
|
|
if (FAILED( hr ) )
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
_pCoreData->szMethod = _Method.QueryStr();
|
|
_pCoreData->cbMethod = _Method.QueryCB() + sizeof(CHAR);
|
|
|
|
//
|
|
// Query string
|
|
//
|
|
|
|
hr = pRequest->GetQueryStringA( &_QueryString );
|
|
|
|
if (FAILED( hr ) )
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
_pCoreData->szQueryString = _QueryString.QueryStr();
|
|
_pCoreData->cbQueryString = _QueryString.QueryCB() + sizeof(CHAR);
|
|
|
|
|
|
//
|
|
// Path translated (and we'll do the UNICODE version while we're at it)
|
|
//
|
|
|
|
hr = pUrlInfo->GetPathTranslated( pW3Context,
|
|
fUsePathInfo,
|
|
&_PathTranslatedW );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
hr = _PathTranslated.CopyW( _PathTranslatedW.QueryStr(),
|
|
_PathTranslatedW.QueryCCH() );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
_pCoreData->szPathTranslated = _PathTranslated.QueryStr();
|
|
_pCoreData->cbPathTranslated = _PathTranslated.QueryCB() + sizeof(CHAR);
|
|
|
|
_pCoreData->szPathTranslatedW = _PathTranslatedW.QueryStr();
|
|
_pCoreData->cbPathTranslatedW = _PathTranslatedW.QueryCB() + sizeof(WCHAR);
|
|
|
|
//
|
|
// Content type
|
|
//
|
|
|
|
_pCoreData->szContentType = (LPSTR)pRequest->GetHeader( HttpHeaderContentType );
|
|
|
|
if ( !_pCoreData->szContentType )
|
|
{
|
|
_pCoreData->szContentType = CONTENT_TYPE_PLACEHOLDER;
|
|
}
|
|
|
|
_pCoreData->cbContentType = (DWORD)strlen( _pCoreData->szContentType ) + sizeof(CHAR);
|
|
|
|
//
|
|
// Connection
|
|
//
|
|
|
|
_pCoreData->szConnection = (LPSTR)pRequest->GetHeader( HttpHeaderConnection );
|
|
|
|
if ( !_pCoreData->szConnection )
|
|
{
|
|
_pCoreData->szConnection = CONNECTION_PLACEHOLDER;
|
|
}
|
|
|
|
_pCoreData->cbConnection = (DWORD)strlen( _pCoreData->szConnection ) + sizeof(CHAR);
|
|
|
|
//
|
|
// UserAgent
|
|
//
|
|
|
|
_pCoreData->szUserAgent = (LPSTR)pRequest->GetHeader( HttpHeaderUserAgent );
|
|
|
|
if ( !_pCoreData->szUserAgent )
|
|
{
|
|
_pCoreData->szUserAgent = USER_AGENT_PLACEHOLDER;
|
|
}
|
|
|
|
_pCoreData->cbUserAgent = (DWORD)strlen( _pCoreData->szUserAgent ) + sizeof(CHAR);
|
|
|
|
//
|
|
// Cookie
|
|
//
|
|
|
|
_pCoreData->szCookie = (LPSTR)pRequest->GetHeader( HttpHeaderCookie );
|
|
|
|
if ( !_pCoreData->szCookie )
|
|
{
|
|
_pCoreData->szCookie = COOKIE_PLACEHOLDER;
|
|
}
|
|
|
|
_pCoreData->cbCookie = (DWORD)strlen( _pCoreData->szCookie ) + sizeof(CHAR);
|
|
|
|
//
|
|
// Appl MD path
|
|
//
|
|
|
|
hr = GetServerVariableApplMdPath( pW3Context, &_ApplMdPath );
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
_pCoreData->szApplMdPath = _ApplMdPath.QueryStr();
|
|
_pCoreData->cbApplMdPath = _ApplMdPath.QueryCB() + sizeof(CHAR);
|
|
|
|
//
|
|
// szApplMdPathW is only populated in
|
|
// the OOF case.
|
|
//
|
|
|
|
_pCoreData->szApplMdPathW = NULL;
|
|
_pCoreData->cbApplMdPathW = 0;
|
|
|
|
//
|
|
// Entity data
|
|
//
|
|
|
|
pW3Context->QueryAlreadyAvailableEntity( &_pCoreData->pAvailableEntity,
|
|
&_pCoreData->cbAvailableEntity );
|
|
|
|
//
|
|
// Keep alive
|
|
//
|
|
|
|
_pCoreData->fAllowKeepAlive = pMetadata->QueryKeepAliveEnabled();
|
|
|
|
//
|
|
// If this is an OOP request, then we need to serialize the core data
|
|
//
|
|
|
|
if ( dwAppType == APP_ISOLATED ||
|
|
dwAppType == APP_POOL )
|
|
{
|
|
hr = SerializeCoreDataForOop( dwAppType );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
DBG_ASSERT( SUCCEEDED( hr ) );
|
|
|
|
return hr;
|
|
|
|
|
|
ErrorExit:
|
|
|
|
DBG_ASSERT( FAILED( hr ) );
|
|
|
|
_pCoreData = NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
W3_ISAPI_HANDLER::SerializeCoreDataForOop(
|
|
DWORD dwAppType
|
|
)
|
|
{
|
|
W3_CONTEXT * pW3Context;
|
|
URL_CONTEXT * pUrlContext;
|
|
W3_URL_INFO * pUrlInfo;
|
|
W3_METADATA * pMetadata;
|
|
STRU * pstru;
|
|
DWORD cbNeeded;
|
|
BYTE * pCursor;
|
|
ISAPI_CORE_DATA * pSerialized;
|
|
HRESULT hr = NO_ERROR;
|
|
|
|
//
|
|
// Caution. This code must be kept in sync
|
|
// with the FixupCoreData function in w3isapi.dll
|
|
//
|
|
|
|
DBG_ASSERT( sm_fWamActive );
|
|
DBG_ASSERT( dwAppType == APP_ISOLATED ||
|
|
dwAppType == APP_POOL );
|
|
|
|
DBG_REQUIRE( ( pW3Context = QueryW3Context() ) != NULL );
|
|
DBG_REQUIRE( ( pUrlContext = pW3Context->QueryUrlContext() ) != NULL );
|
|
DBG_REQUIRE( ( pUrlInfo = pUrlContext->QueryUrlInfo() ) != NULL );
|
|
DBG_REQUIRE( ( pMetadata = pUrlContext->QueryMetaData() ) != NULL );
|
|
|
|
//
|
|
// Set the WAM information and NULL out the pSid dude
|
|
//
|
|
|
|
if ( dwAppType == APP_ISOLATED )
|
|
{
|
|
pstru = pMetadata->QueryWamClsId();
|
|
|
|
DBG_ASSERT( pstru );
|
|
|
|
if ( pstru == NULL )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND );
|
|
goto ErrorExit;
|
|
}
|
|
|
|
wcsncpy( _pCoreData->szWamClsid,
|
|
pstru->QueryStr(),
|
|
SIZE_CLSID_STRING );
|
|
|
|
_pCoreData->szWamClsid[SIZE_CLSID_STRING-1] = L'\0';
|
|
}
|
|
else
|
|
{
|
|
wcsncpy( _pCoreData->szWamClsid,
|
|
POOL_WAM_CLSID,
|
|
SIZE_CLSID_STRING );
|
|
|
|
_pCoreData->szWamClsid[SIZE_CLSID_STRING-1] = L'\0';
|
|
}
|
|
|
|
_pCoreData->fIsOop = TRUE;
|
|
|
|
_pCoreData->pSid = NULL;
|
|
|
|
//
|
|
// ApplMdPathW
|
|
//
|
|
|
|
hr = GetServerVariableApplMdPathW( pW3Context, &_ApplMdPathW );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
_pCoreData->szApplMdPathW = _ApplMdPathW.QueryStr();
|
|
_pCoreData->cbApplMdPathW = _ApplMdPathW.QueryCB() + sizeof(WCHAR);
|
|
|
|
//
|
|
// Calculate the size needed for the core data and
|
|
// set up the buffer to contain it.
|
|
//
|
|
|
|
cbNeeded = sizeof(ISAPI_CORE_DATA);
|
|
|
|
cbNeeded += _pCoreData->cbGatewayImage;
|
|
cbNeeded += _pCoreData->cbPhysicalPath;
|
|
cbNeeded += _pCoreData->cbPathInfo;
|
|
cbNeeded += _pCoreData->cbMethod;
|
|
cbNeeded += _pCoreData->cbQueryString;
|
|
cbNeeded += _pCoreData->cbPathTranslated;
|
|
cbNeeded += _pCoreData->cbContentType;
|
|
cbNeeded += _pCoreData->cbConnection;
|
|
cbNeeded += _pCoreData->cbUserAgent;
|
|
cbNeeded += _pCoreData->cbCookie;
|
|
cbNeeded += _pCoreData->cbApplMdPath;
|
|
cbNeeded += _pCoreData->cbApplMdPathW;
|
|
cbNeeded += _pCoreData->cbPathTranslatedW;
|
|
cbNeeded += _pCoreData->cbAvailableEntity;
|
|
|
|
hr = _buffCoreData.Resize( cbNeeded );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Copy the core data into the buffer
|
|
//
|
|
|
|
pSerialized = (ISAPI_CORE_DATA*)_buffCoreData.QueryPtr();
|
|
pCursor = (BYTE*)pSerialized;
|
|
|
|
CopyMemory( pCursor,
|
|
_pCoreData,
|
|
sizeof(ISAPI_CORE_DATA) );
|
|
|
|
pSerialized->cbSize = cbNeeded;
|
|
|
|
pCursor += sizeof(ISAPI_CORE_DATA);
|
|
|
|
//
|
|
// Add gateway image
|
|
//
|
|
|
|
pSerialized->szGatewayImage = (LPWSTR)pCursor;
|
|
|
|
CopyMemory( pSerialized->szGatewayImage,
|
|
_pCoreData->szGatewayImage,
|
|
_pCoreData->cbGatewayImage );
|
|
|
|
pCursor += _pCoreData->cbGatewayImage;
|
|
|
|
//
|
|
// Add ApplMdPathW
|
|
//
|
|
|
|
pSerialized->szApplMdPathW = (LPWSTR)pCursor;
|
|
|
|
CopyMemory( pSerialized->szApplMdPathW,
|
|
_pCoreData->szApplMdPathW,
|
|
_pCoreData->cbApplMdPathW );
|
|
|
|
pCursor += _pCoreData->cbApplMdPathW;
|
|
|
|
//
|
|
// Add PathTranslatedW
|
|
//
|
|
|
|
pSerialized->szPathTranslatedW = (LPWSTR)pCursor;
|
|
|
|
CopyMemory( pSerialized->szPathTranslatedW,
|
|
_pCoreData->szPathTranslatedW,
|
|
_pCoreData->cbPathTranslatedW );
|
|
|
|
pCursor += _pCoreData->cbPathTranslatedW;
|
|
|
|
//
|
|
// Add physical path
|
|
//
|
|
|
|
pSerialized->szPhysicalPath = (LPSTR)pCursor;
|
|
|
|
CopyMemory( pSerialized->szPhysicalPath,
|
|
_pCoreData->szPhysicalPath,
|
|
_pCoreData->cbPhysicalPath );
|
|
|
|
pCursor += _pCoreData->cbPhysicalPath;
|
|
|
|
//
|
|
// Add path info
|
|
//
|
|
|
|
pSerialized->szPathInfo = (LPSTR)pCursor;
|
|
|
|
CopyMemory( pSerialized->szPathInfo,
|
|
_pCoreData->szPathInfo,
|
|
_pCoreData->cbPathInfo );
|
|
|
|
pCursor += _pCoreData->cbPathInfo;
|
|
|
|
//
|
|
// Add method
|
|
//
|
|
|
|
pSerialized->szMethod = (LPSTR)pCursor;
|
|
|
|
CopyMemory( pSerialized->szMethod,
|
|
_pCoreData->szMethod,
|
|
_pCoreData->cbMethod );
|
|
|
|
pCursor += _pCoreData->cbMethod;
|
|
|
|
//
|
|
// Add query string
|
|
//
|
|
|
|
pSerialized->szQueryString = (LPSTR)pCursor;
|
|
|
|
CopyMemory( pSerialized->szQueryString,
|
|
_pCoreData->szQueryString,
|
|
_pCoreData->cbQueryString );
|
|
|
|
pCursor += _pCoreData->cbQueryString;
|
|
|
|
//
|
|
// Add path translated
|
|
//
|
|
|
|
pSerialized->szPathTranslated = (LPSTR)pCursor;
|
|
|
|
CopyMemory( pSerialized->szPathTranslated,
|
|
_pCoreData->szPathTranslated,
|
|
_pCoreData->cbPathTranslated );
|
|
|
|
pCursor += _pCoreData->cbPathTranslated;
|
|
|
|
//
|
|
// Add content type
|
|
//
|
|
|
|
pSerialized->szContentType = (LPSTR)pCursor;
|
|
|
|
CopyMemory( pSerialized->szContentType,
|
|
_pCoreData->szContentType,
|
|
_pCoreData->cbContentType );
|
|
|
|
pCursor += _pCoreData->cbContentType;
|
|
|
|
//
|
|
// Add connection
|
|
//
|
|
|
|
pSerialized->szConnection = (LPSTR)pCursor;
|
|
|
|
CopyMemory( pSerialized->szConnection,
|
|
_pCoreData->szConnection,
|
|
_pCoreData->cbConnection );
|
|
|
|
pCursor += _pCoreData->cbConnection;
|
|
|
|
//
|
|
// Add user agent
|
|
//
|
|
|
|
pSerialized->szUserAgent = (LPSTR)pCursor;
|
|
|
|
CopyMemory( pSerialized->szUserAgent,
|
|
_pCoreData->szUserAgent,
|
|
_pCoreData->cbUserAgent );
|
|
|
|
pCursor += _pCoreData->cbUserAgent;
|
|
|
|
//
|
|
// Add cookie
|
|
//
|
|
|
|
pSerialized->szCookie = (LPSTR)pCursor;
|
|
|
|
CopyMemory( pSerialized->szCookie,
|
|
_pCoreData->szCookie,
|
|
_pCoreData->cbCookie );
|
|
|
|
pCursor += _pCoreData->cbCookie;
|
|
|
|
//
|
|
// Add ApplMdPath
|
|
//
|
|
|
|
pSerialized->szApplMdPath = (LPSTR)pCursor;
|
|
|
|
CopyMemory( pSerialized->szApplMdPath,
|
|
_pCoreData->szApplMdPath,
|
|
_pCoreData->cbApplMdPath );
|
|
|
|
pCursor += _pCoreData->cbApplMdPath;
|
|
|
|
//
|
|
// Add entity data
|
|
//
|
|
|
|
if ( _pCoreData->cbAvailableEntity )
|
|
{
|
|
pSerialized->pAvailableEntity = (LPWSTR)pCursor;
|
|
|
|
CopyMemory( pSerialized->pAvailableEntity,
|
|
_pCoreData->pAvailableEntity,
|
|
_pCoreData->cbAvailableEntity );
|
|
}
|
|
|
|
//
|
|
// Point _pCoreData at the new buffer and we're done
|
|
//
|
|
|
|
_pCoreData = pSerialized;
|
|
|
|
DBG_ASSERT( SUCCEEDED( hr ) );
|
|
|
|
return hr;
|
|
|
|
ErrorExit:
|
|
|
|
DBG_ASSERT( FAILED( hr ) );
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
W3_ISAPI_HANDLER::DuplicateWamProcessHandleForLocalUse(
|
|
HANDLE hWamProcessHandle,
|
|
HANDLE * phLocalHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Duplicates a handle defined in a WAM process to a local
|
|
handle useful in the IIS core
|
|
|
|
Arguments:
|
|
|
|
hWamProcessHandle - The value of the handle from the WAM process
|
|
phLocalHandle - Upon successful return, the handle useable in
|
|
the core process
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
HANDLE hWamProcess;
|
|
HRESULT hr = NOERROR;
|
|
|
|
DBG_ASSERT( _pWamProcess );
|
|
|
|
DBG_REQUIRE( ( hWamProcess = _pWamProcess->QueryProcess() ) != NULL );
|
|
|
|
if ( !DuplicateHandle( hWamProcess, hWamProcessHandle, GetCurrentProcess(),
|
|
phLocalHandle, 0, FALSE, DUPLICATE_SAME_ACCESS ) )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
W3_ISAPI_HANDLER::MarshalAsyncReadBuffer(
|
|
DWORD64 pWamExecInfo,
|
|
LPBYTE pBuffer,
|
|
DWORD cbBuffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Pushes a buffer into a WAM process. This function is called
|
|
to copy a local read buffer into the WAM process just prior
|
|
to notifying the I/O completion function of an OOP extension.
|
|
|
|
Arguments:
|
|
|
|
pWamExecInfo - A WAM_EXEC_INFO pointer that identifies the request
|
|
to the OOP host.
|
|
pBuffer - The buffer to copy
|
|
cbBuffer - The amount of data in pBuffer
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
DBG_ASSERT( sm_fWamActive );
|
|
DBG_ASSERT( _pWamProcess );
|
|
|
|
return _pWamProcess->MarshalAsyncReadBuffer( pWamExecInfo, pBuffer,
|
|
cbBuffer );
|
|
}
|
|
|
|
VOID
|
|
W3_ISAPI_HANDLER::IsapiRequestFinished(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called by the destructor for the
|
|
ISAPI_REQUEST associated with this request. If the
|
|
current state of the W3_ISAPI_HANDLER is
|
|
ISAPI_STATE_PENDING, then it will advance the core
|
|
state machine.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
W3_CONTEXT * pW3Context = QueryW3Context();
|
|
HRESULT hr = NO_ERROR;
|
|
|
|
DBG_ASSERT( pW3Context );
|
|
|
|
if ( _State == ISAPI_STATE_FAILED )
|
|
{
|
|
if ( pW3Context->QueryResponseSent() == FALSE )
|
|
{
|
|
//
|
|
// The ISAPI didn't send a response, we need to send
|
|
// it now. If our send is successful, we should return
|
|
// immediately.
|
|
//
|
|
|
|
hr = pW3Context->SendResponse( W3_FLAG_ASYNC );
|
|
|
|
if ( SUCCEEDED( hr ) )
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
pW3Context->SetErrorStatus( hr );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Since we didn't end up sending a response, we need
|
|
// to set the status to pending. This will result in
|
|
// the below code triggering a completion to clean up
|
|
// the state machine.
|
|
//
|
|
|
|
_State = ISAPI_STATE_PENDING;
|
|
}
|
|
|
|
if ( _State == ISAPI_STATE_PENDING )
|
|
{
|
|
RestartCoreStateMachine();
|
|
}
|
|
}
|
|
|
|
VOID
|
|
W3_ISAPI_HANDLER::RestartCoreStateMachine(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Advances the core state machine by setting state
|
|
to ISAPI_STATE_DONE and triggering an I/O completion.
|
|
|
|
Note that this function is only expected to be called
|
|
if the object state is ISAPI_STATE_PENDING.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
W3_CONTEXT * pW3Context = QueryW3Context();
|
|
|
|
DBG_ASSERT( pW3Context );
|
|
DBG_ASSERT( _State == ISAPI_STATE_PENDING );
|
|
|
|
//
|
|
// Need to set state to ISAPI_STATE_DONE so that the
|
|
// resulting completion does the advance for us.
|
|
//
|
|
|
|
_State = ISAPI_STATE_DONE;
|
|
|
|
//
|
|
// If this is the main request, then we can resume the state
|
|
// machine on the ISAPIs thread. Therefore no need to marshall
|
|
// to another thread. If it is not the main request, the we
|
|
// don't want to risk doing a long running operation (i.e. the
|
|
// completion of the parent request) on the ISAPI thread
|
|
//
|
|
|
|
if ( pW3Context == pW3Context->QueryMainContext() )
|
|
{
|
|
W3_MAIN_CONTEXT::OnPostedCompletion( NO_ERROR,
|
|
0,
|
|
(OVERLAPPED*) pW3Context->QueryMainContext() );
|
|
}
|
|
else
|
|
{
|
|
POST_MAIN_COMPLETION( pW3Context->QueryMainContext() );
|
|
}
|
|
}
|
|
|
|
//static
|
|
HRESULT
|
|
W3_ISAPI_HANDLER::Initialize(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes W3_ISAPI_HANDLER
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, "W3_ISAPI_HANDLER::Initialize()\n" ));
|
|
|
|
//
|
|
// For debugging purposes, create a unique instance ID for this
|
|
// instance of the handler.
|
|
//
|
|
|
|
#ifdef DBG
|
|
|
|
UUID uuid;
|
|
RPC_STATUS rpcStatus;
|
|
unsigned char * szRpcString;
|
|
|
|
rpcStatus = UuidCreate( &uuid );
|
|
|
|
if ( (rpcStatus != RPC_S_OK) && (rpcStatus != RPC_S_UUID_LOCAL_ONLY) )
|
|
{
|
|
SetLastError( rpcStatus );
|
|
goto error_exit;
|
|
}
|
|
|
|
rpcStatus = UuidToStringA( &uuid, &szRpcString );
|
|
|
|
if ( rpcStatus != RPC_S_OK )
|
|
{
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
goto error_exit;
|
|
}
|
|
|
|
strncpy( sm_szInstanceId, (LPSTR)szRpcString, SIZE_CLSID_STRING );
|
|
sm_szInstanceId[SIZE_CLSID_STRING - 1] = '\0';
|
|
|
|
RpcStringFreeA( &szRpcString );
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT,
|
|
"W3_ISAPI_HANDLER initialized instance %s.\r\n",
|
|
sm_szInstanceId
|
|
));
|
|
|
|
#else
|
|
|
|
sm_szInstanceId[0] = '\0';
|
|
|
|
#endif _DBG
|
|
|
|
PFN_ISAPI_INIT_MODULE pfnInit = NULL;
|
|
|
|
sm_hIsapiModule = LoadLibrary( ISAPI_MODULE_NAME );
|
|
if( sm_hIsapiModule == NULL )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto error_exit;
|
|
}
|
|
|
|
sm_pfnTermIsapiModule =
|
|
(PFN_ISAPI_TERM_MODULE)GetProcAddress( sm_hIsapiModule,
|
|
ISAPI_TERM_MODULE
|
|
);
|
|
|
|
sm_pfnProcessIsapiRequest =
|
|
(PFN_ISAPI_PROCESS_REQUEST)GetProcAddress( sm_hIsapiModule,
|
|
ISAPI_PROCESS_REQUEST
|
|
);
|
|
|
|
sm_pfnProcessIsapiCompletion =
|
|
(PFN_ISAPI_PROCESS_COMPLETION)GetProcAddress( sm_hIsapiModule,
|
|
ISAPI_PROCESS_COMPLETION
|
|
);
|
|
|
|
if( !sm_pfnTermIsapiModule ||
|
|
!sm_pfnProcessIsapiRequest ||
|
|
!sm_pfnProcessIsapiCompletion )
|
|
{
|
|
hr = E_FAIL;
|
|
goto error_exit;
|
|
}
|
|
|
|
pfnInit =
|
|
(PFN_ISAPI_INIT_MODULE)GetProcAddress( sm_hIsapiModule,
|
|
ISAPI_INIT_MODULE
|
|
);
|
|
if( !pfnInit )
|
|
{
|
|
hr = E_FAIL;
|
|
goto error_exit;
|
|
}
|
|
|
|
hr = pfnInit(
|
|
NULL,
|
|
sm_szInstanceId,
|
|
GetCurrentProcessId()
|
|
);
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
goto error_exit;
|
|
}
|
|
|
|
DBG_REQUIRE( ISAPI_REQUEST::InitClass() );
|
|
|
|
sm_pInprocIsapiHash = NULL;
|
|
|
|
//
|
|
// If we're running in backward compatibility mode, initialize
|
|
// the WAM process manager and inprocess ISAPI app list
|
|
//
|
|
|
|
if ( g_pW3Server->QueryInBackwardCompatibilityMode() )
|
|
{
|
|
WCHAR szIsapiModule[MAX_PATH];
|
|
//
|
|
// Store away the full path to the loaded ISAPI module
|
|
// so that we can pass it to OOP processes so that they
|
|
// know how to load it
|
|
//
|
|
|
|
if ( GetModuleFileNameW(
|
|
GetModuleHandleW( ISAPI_MODULE_NAME ),
|
|
szIsapiModule,
|
|
MAX_PATH
|
|
) == 0 )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto error_exit;
|
|
}
|
|
|
|
DBG_ASSERT( szIsapiModule[0] != '\0' );
|
|
|
|
//
|
|
// Initialize the WAM_PROCESS_MANAGER
|
|
//
|
|
|
|
sm_pWamProcessManager = new WAM_PROCESS_MANAGER( szIsapiModule );
|
|
|
|
if ( !sm_pWamProcessManager )
|
|
{
|
|
goto error_exit;
|
|
}
|
|
|
|
hr = sm_pWamProcessManager->Create();
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
sm_pWamProcessManager->Release();
|
|
sm_pWamProcessManager = NULL;
|
|
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Hook up wamreg
|
|
//
|
|
|
|
hr = WamReg_RegisterSinkNotify( W3SVC_WamRegSink );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto error_exit;
|
|
}
|
|
|
|
INITIALIZE_CRITICAL_SECTION( &sm_csInprocHashLock );
|
|
|
|
UpdateInprocIsapiHash();
|
|
|
|
INITIALIZE_CRITICAL_SECTION( &sm_csBigHurkinWamRegLock );
|
|
|
|
sm_fWamActive = TRUE;
|
|
}
|
|
else
|
|
{
|
|
sm_pWamProcessManager = NULL;
|
|
sm_fWamActive = FALSE;
|
|
}
|
|
|
|
sg_Initialized = TRUE;
|
|
|
|
return hr;
|
|
error_exit:
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"W3_ISAPI_HANDLER::Initialize() Error=%08x\n",
|
|
hr
|
|
));
|
|
|
|
if ( sm_hIsapiModule )
|
|
{
|
|
FreeLibrary( sm_hIsapiModule );
|
|
sm_hIsapiModule = NULL;
|
|
}
|
|
|
|
sm_pfnTermIsapiModule = NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
//static
|
|
VOID
|
|
W3_ISAPI_HANDLER::Terminate(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Terminates W3_ISAPI_HANDLER
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT, "W3_ISAPI_HANDLER::Terminate()\n" ));
|
|
|
|
sg_Initialized = FALSE;
|
|
|
|
DBG_ASSERT( sm_pfnTermIsapiModule );
|
|
DBG_ASSERT( sm_hIsapiModule );
|
|
|
|
if( sm_pfnTermIsapiModule )
|
|
{
|
|
sm_pfnTermIsapiModule();
|
|
sm_pfnTermIsapiModule = NULL;
|
|
}
|
|
|
|
if( sm_hIsapiModule )
|
|
{
|
|
FreeLibrary( sm_hIsapiModule );
|
|
sm_hIsapiModule = NULL;
|
|
}
|
|
|
|
if ( sm_pInprocIsapiHash )
|
|
{
|
|
delete sm_pInprocIsapiHash;
|
|
}
|
|
|
|
if ( sm_fWamActive )
|
|
{
|
|
//
|
|
// Disconnect wamreg
|
|
//
|
|
|
|
WamReg_UnRegisterSinkNotify();
|
|
|
|
if ( sm_pWamProcessManager )
|
|
{
|
|
sm_pWamProcessManager->Shutdown();
|
|
|
|
sm_pWamProcessManager->Release();
|
|
sm_pWamProcessManager = NULL;
|
|
}
|
|
|
|
DeleteCriticalSection( &sm_csInprocHashLock );
|
|
DeleteCriticalSection( &sm_csBigHurkinWamRegLock );
|
|
}
|
|
|
|
ISAPI_REQUEST::CleanupClass();
|
|
}
|
|
|
|
// static
|
|
HRESULT
|
|
W3_ISAPI_HANDLER::W3SVC_WamRegSink(
|
|
LPCSTR szAppPath,
|
|
const DWORD dwCommand,
|
|
DWORD * pdwResult
|
|
)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
WAM_PROCESS * pWamProcess = NULL;
|
|
WAM_APP_INFO * pWamAppInfo = NULL;
|
|
DWORD dwWamSubError = 0;
|
|
BOOL fIsLoaded = FALSE;
|
|
|
|
//
|
|
// Scary monsters live in the land where this function
|
|
// is allowed to run willy nilly
|
|
//
|
|
|
|
LockWamReg();
|
|
|
|
DBG_ASSERT( szAppPath );
|
|
DBG_ASSERT( sm_pWamProcessManager );
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT,
|
|
"WAM_PROCESS_MANAGER received a Sink Notify on MD path %S, cmd = %d.\r\n",
|
|
(LPCWSTR)szAppPath,
|
|
dwCommand
|
|
));
|
|
|
|
*pdwResult = APPSTATUS_UnLoaded;
|
|
|
|
switch ( dwCommand )
|
|
{
|
|
case APPCMD_UNLOAD:
|
|
case APPCMD_DELETE:
|
|
case APPCMD_CHANGETOINPROC:
|
|
case APPCMD_CHANGETOOUTPROC:
|
|
|
|
//
|
|
// Unload the specified wam process.
|
|
//
|
|
// Note that we're casting the incoming app path to
|
|
// UNICODE. This is because wamreg would normally
|
|
// convert the MD path (which is nativly UNICODE) to
|
|
// MBCS in IIS 5.x. It's smart enough to know that
|
|
// for 6.0 we want to work directly with UNICODE.
|
|
//
|
|
|
|
hr = sm_pWamProcessManager->GetWamProcessInfo(
|
|
reinterpret_cast<LPCWSTR>(szAppPath),
|
|
&pWamAppInfo,
|
|
&fIsLoaded
|
|
);
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Done;
|
|
}
|
|
|
|
DBG_ASSERT( pWamAppInfo );
|
|
|
|
//
|
|
// If the app has not been loaded by the WAM_PROCESS_MANAGER
|
|
// then there is nothing more to do.
|
|
//
|
|
|
|
if ( fIsLoaded == FALSE )
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = sm_pWamProcessManager->GetWamProcess(
|
|
pWamAppInfo->_szClsid,
|
|
pWamAppInfo->_szAppPath,
|
|
&dwWamSubError,
|
|
&pWamProcess,
|
|
sm_szInstanceId
|
|
);
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
//
|
|
// Hey, this app was loaded just a moment ago!
|
|
//
|
|
|
|
DBG_ASSERT( FALSE );
|
|
goto Done;
|
|
}
|
|
|
|
DBG_ASSERT( pWamProcess );
|
|
|
|
hr = pWamProcess->Unload( 0 );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Done;
|
|
}
|
|
|
|
break;
|
|
|
|
case APPCMD_GETSTATUS:
|
|
|
|
hr = sm_pWamProcessManager->GetWamProcessInfo(
|
|
reinterpret_cast<LPCWSTR>(szAppPath),
|
|
&pWamAppInfo,
|
|
&fIsLoaded
|
|
);
|
|
|
|
if ( SUCCEEDED( hr ) )
|
|
{
|
|
if ( fIsLoaded )
|
|
{
|
|
*pdwResult = APPSTATUS_Running;
|
|
}
|
|
else
|
|
{
|
|
*pdwResult = APPSTATUS_Stopped;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
Done:
|
|
|
|
UnlockWamReg();
|
|
|
|
if ( pWamAppInfo )
|
|
{
|
|
pWamAppInfo->Release();
|
|
pWamAppInfo = NULL;
|
|
}
|
|
|
|
if ( pWamProcess )
|
|
{
|
|
pWamProcess->Release();
|
|
pWamProcess = NULL;
|
|
}
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
*pdwResult = APPSTATUS_Error;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// static
|
|
HRESULT
|
|
W3_ISAPI_HANDLER::UpdateInprocIsapiHash(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Updates the table of InProcessIsapiApps
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
MB mb( g_pW3Server->QueryMDObject() );
|
|
W3_INPROC_ISAPI_HASH * pNewTable = NULL;
|
|
W3_INPROC_ISAPI_HASH * pOldTable = NULL;
|
|
DWORD i;
|
|
LPWSTR psz;
|
|
HRESULT hr = NOERROR;
|
|
LK_RETCODE lkr = LK_SUCCESS;
|
|
|
|
//
|
|
// Allocate a new table and populate it.
|
|
//
|
|
|
|
pNewTable = new W3_INPROC_ISAPI_HASH;
|
|
|
|
if ( !pNewTable )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if ( !mb.Open( L"/LM/W3SVC/" ) )
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if ( !mb.GetMultisz( L"",
|
|
MD_IN_PROCESS_ISAPI_APPS,
|
|
IIS_MD_UT_SERVER,
|
|
&pNewTable->_mszImages) )
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
mb.Close();
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Merge ISAPI filter images into the list
|
|
//
|
|
|
|
AddAllFiltersToMultiSz( mb, &pNewTable->_mszImages );
|
|
|
|
mb.Close();
|
|
|
|
//
|
|
// Now that we have a complete list, add them to the
|
|
// hash table.
|
|
//
|
|
|
|
for ( i = 0, psz = (LPWSTR)pNewTable->_mszImages.First();
|
|
psz != NULL;
|
|
i++, psz = (LPWSTR)pNewTable->_mszImages.Next( psz ) )
|
|
{
|
|
W3_INPROC_ISAPI * pNewRecord;
|
|
|
|
//
|
|
// Allocate a new W3_INPROC_ISAPI object and add
|
|
// it to the table
|
|
//
|
|
|
|
pNewRecord = new W3_INPROC_ISAPI;
|
|
|
|
if ( !pNewRecord )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
|
|
goto ErrorExit;
|
|
}
|
|
|
|
hr = pNewRecord->Create( psz );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
pNewRecord->DereferenceInprocIsapi();
|
|
pNewRecord = NULL;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
lkr = pNewTable->InsertRecord( pNewRecord, TRUE );
|
|
|
|
pNewRecord->DereferenceInprocIsapi();
|
|
pNewRecord = NULL;
|
|
|
|
if ( lkr != LK_SUCCESS && lkr != LK_KEY_EXISTS )
|
|
{
|
|
hr = E_FAIL;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now swap in the new table and delete the old one
|
|
//
|
|
|
|
EnterCriticalSection( &sm_csInprocHashLock );
|
|
|
|
pOldTable = sm_pInprocIsapiHash;
|
|
sm_pInprocIsapiHash = pNewTable;
|
|
|
|
LeaveCriticalSection( &sm_csInprocHashLock );
|
|
|
|
if ( pOldTable )
|
|
{
|
|
delete pOldTable;
|
|
}
|
|
|
|
DBG_ASSERT( SUCCEEDED( hr ) );
|
|
|
|
return hr;
|
|
|
|
|
|
ErrorExit:
|
|
|
|
DBG_ASSERT( FAILED( hr ) );
|
|
|
|
if ( pNewTable )
|
|
{
|
|
delete pNewTable;
|
|
};
|
|
|
|
return hr;
|
|
}
|
|
|
|
VOID
|
|
AddFiltersToMultiSz(
|
|
IN const MB & mb,
|
|
IN LPCWSTR szFilterPath,
|
|
IN OUT MULTISZ * pmsz
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
Add the ISAPI filters at the specified metabase path to pmsz.
|
|
|
|
Called by AddAllFiltersToMultiSz.
|
|
|
|
Arguments:
|
|
mb metabase key open to /LM/W3SVC
|
|
szFilterPath path of /Filters key relative to /LM/W3SVC
|
|
pmsz multisz containing the in proc dlls
|
|
|
|
Return:
|
|
Nothing - failure cases ignored.
|
|
|
|
--*/
|
|
{
|
|
WCHAR szKeyName[MAX_PATH + 1];
|
|
STRU strFilterPath;
|
|
STRU strFullKeyName;
|
|
INT pchFilterPath = (INT)wcslen( szFilterPath );
|
|
|
|
if ( FAILED( strFullKeyName.Copy( szFilterPath ) ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
DWORD i = 0;
|
|
|
|
if( SUCCEEDED( strFullKeyName.Append( L"/", 1 ) ) )
|
|
{
|
|
while ( const_cast<MB &>(mb).EnumObjects( szFilterPath,
|
|
szKeyName,
|
|
i++ ) )
|
|
{
|
|
|
|
if( SUCCEEDED( strFullKeyName.Append( szKeyName ) ) )
|
|
{
|
|
if( const_cast<MB &>(mb).GetStr( strFullKeyName.QueryStr(),
|
|
MD_FILTER_IMAGE_PATH,
|
|
IIS_MD_UT_SERVER,
|
|
&strFilterPath ) )
|
|
{
|
|
pmsz->Append( strFilterPath );
|
|
}
|
|
}
|
|
strFullKeyName.SetLen( pchFilterPath + 1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
AddAllFiltersToMultiSz(
|
|
IN const MB & mb,
|
|
IN OUT MULTISZ * pmsz
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This is designed to prevent ISAPI extension/filter
|
|
combination dlls from running out of process.
|
|
|
|
Add the base set of filters defined for the service to pmsz.
|
|
Iterate through the sites and add the filters defined for
|
|
each site.
|
|
|
|
Arguments:
|
|
|
|
mb metabase key open to /LM/W3SVC
|
|
pmsz multisz containing the in proc dlls
|
|
|
|
Return:
|
|
Nothing - failure cases ignored.
|
|
|
|
--*/
|
|
{
|
|
WCHAR szKeyName[MAX_PATH + 1];
|
|
STRU strFullKeyName;
|
|
DWORD i = 0;
|
|
DWORD dwInstanceId = 0;
|
|
|
|
if ( FAILED( strFullKeyName.Copy( L"/" ) ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
AddFiltersToMultiSz( mb, L"/Filters", pmsz );
|
|
|
|
while ( const_cast<MB &>(mb).EnumObjects( L"",
|
|
szKeyName,
|
|
i++ ) )
|
|
{
|
|
dwInstanceId = _wtoi( szKeyName );
|
|
if( 0 != dwInstanceId )
|
|
{
|
|
// This is a site.
|
|
if( SUCCEEDED( strFullKeyName.Append( szKeyName ) ) &&
|
|
SUCCEEDED( strFullKeyName.Append( L"/Filters" ) ) )
|
|
{
|
|
AddFiltersToMultiSz( mb, strFullKeyName.QueryStr(), pmsz );
|
|
}
|
|
|
|
strFullKeyName.SetLen( 1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT
|
|
W3_ISAPI_HANDLER::SetupUlCachedResponse(
|
|
W3_CONTEXT * pW3Context,
|
|
HTTP_CACHE_POLICY *pCachePolicy
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Setup a response to be cached by UL.
|
|
|
|
Arguments:
|
|
|
|
pW3Context - Context
|
|
pCachePolicy - Cache policy to be filled in if caching desired
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
STACK_STRU( strFlushUrl, MAX_PATH );
|
|
HRESULT hr;
|
|
DWORD dwTTL = 0;
|
|
|
|
if ( pW3Context == NULL )
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// Find out if caching is enabled for this isapi request
|
|
//
|
|
if (!_pIsapiRequest->QueryCacheResponse())
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
|
|
}
|
|
|
|
W3_RESPONSE *pResponse = pW3Context->QueryResponse();
|
|
LPCSTR pszExpires = pResponse->GetHeader( HttpHeaderExpires );
|
|
if ( pszExpires != NULL )
|
|
{
|
|
LARGE_INTEGER liExpireTime;
|
|
LARGE_INTEGER liCurrentTime;
|
|
if ( !StringTimeToFileTime( pszExpires,
|
|
&liExpireTime ) )
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
|
|
}
|
|
|
|
GetSystemTimeAsFileTime( (FILETIME *)&liCurrentTime );
|
|
|
|
if ( liExpireTime.QuadPart <= liCurrentTime.QuadPart )
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
|
|
}
|
|
|
|
dwTTL = ( liExpireTime.QuadPart - liCurrentTime.QuadPart ) / 10000000;
|
|
}
|
|
|
|
//
|
|
// Get the exact URL used to flush UL cache
|
|
//
|
|
hr = pW3Context->QueryMainContext()->QueryRequest()->GetOriginalFullUrl(
|
|
&strFlushUrl );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Setup UL cache response token
|
|
//
|
|
|
|
DBG_ASSERT( g_pW3Server->QueryUlCache() != NULL );
|
|
|
|
hr = g_pW3Server->QueryUlCache()->SetupUlCachedResponse(
|
|
pW3Context,
|
|
strFlushUrl,
|
|
FALSE,
|
|
NULL );
|
|
if ( SUCCEEDED( hr ) )
|
|
{
|
|
if (pszExpires == NULL)
|
|
{
|
|
pCachePolicy->Policy = HttpCachePolicyUserInvalidates;
|
|
}
|
|
else
|
|
{
|
|
pCachePolicy->Policy = HttpCachePolicyTimeToLive;
|
|
pCachePolicy->SecondsToLive = dwTTL;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|