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.
 
 
 
 
 
 

625 lines
13 KiB

/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
ssi_file.hxx
Abstract:
This module contains the server side include processing code. We
aim for support as specified by iis\spec\ssi.doc. The code is based
on existing SSI support done in iis\svcs\w3\gateways\ssinc\ssinc.cxx.
SSI_FILE class handles all the details file access.
File cache of W3CORE is leveraged to cache STM files
( using W3CORE file cache directly doesn't necessarily conform
with ISAPI rules because we use private hooks not officially
exposed to ISAPIs )
Author:
Ming Lu (MingLu) 5-Apr-2000
Revision history
Jaroslad Dec-2000
- modified to execute asynchronously
Jaroslad Apr-2001
- added VectorSend support, keepalive, split to multiple source files
--*/
#include "precomp.hxx"
//
// access to W3CORE cache
//
//static
W3_FILE_INFO_CACHE * SSI_FILE::s_pFileCache = NULL;
//static
HRESULT
SSI_FILE::InitializeGlobals(
VOID
)
/*++
Routine Description:
Initialization of globals
Arguments:
Return Value:
HRESULT
--*/
{
//
// Get the cache instance for W3CORE.DLL
//
s_pFileCache = W3_FILE_INFO_CACHE::GetFileCache();
if ( s_pFileCache == NULL )
{
return HRESULT_FROM_WIN32( ERROR_NOT_FOUND );
}
return S_OK;
}
//static
VOID
SSI_FILE::TerminateGlobals(
VOID
)
/*++
Routine Description:
Cleanup of globals
Arguments:
Return Value:
VOID
--*/
{
//
// ssinc.dll is using file cache from w3core
// All objects associated with file cache must be cleaned up
// before letting to unload this DLL
//
W3CacheFlushAllCaches();
}
SSI_FILE::SSI_FILE(
IN W3_FILE_INFO * pOpenFile
) :
_hMapHandle ( NULL ),
_pvMappedBase ( NULL ),
_fResponseHeadersIncludeContentLength( FALSE ),
_strResponseHeaders( _abResponseHeaders, sizeof(_abResponseHeaders) ),
_pFile ( pOpenFile )
{
DBG_ASSERT( _pFile != NULL );
_dwSignature = SSI_FILE_SIGNATURE;
}
SSI_FILE::~SSI_FILE()
{
DBG_ASSERT( CheckSignature() );
_dwSignature = SSI_FILE_SIGNATURE_FREED;
//
// delete File Memory Mapping if still happens to be around
//
SSIDeleteFileMapping();
//
// reset backpointer to W3_FILE_INFO,
// no cleanup is needed, because W3_FILE_INFO actually
// controls the lifetime of SSI_FILE (through the Cleanup() call)
// once SSI_FILE instance is associated with W3_FILE_INFO
//
_pFile = NULL;
if ( _pSsiElementList != NULL )
{
delete _pSsiElementList;
_pSsiElementList = NULL;
}
}
//static
HRESULT
SSI_FILE::GetReferencedInstance(
IN STRU& strFilename,
HANDLE hUserToken,
OUT SSI_FILE ** ppSsiFile
)
/*++
Routine Description:
Lookup SSI_FILE
It builds the Server Side Include Element List the first
time a .stm file is sent. Subsequently, it is
checked out from the associated cache blob.
SSI_FILE is refcounted indirectly by association with the
W3_FILE_INFO
Once GetReferencedInstance() returns successfully then the
DereferenceSsiFile() must be called to assure proper cleanup
Arguments:
Return Value:
HRESULT
--*/
{
HRESULT hr = E_FAIL;
CACHE_USER fileUser;
SSI_FILE * pSsiFile = NULL;
W3_FILE_INFO * pOpenFile = NULL;
DBG_ASSERT( ppSsiFile != NULL );
fileUser._hToken = hUserToken;
hr = s_pFileCache->GetFileInfo( strFilename,
NULL,
&fileUser,
TRUE,
&pOpenFile );
if ( FAILED( hr ) )
{
goto failed;
}
DBG_ASSERT( pOpenFile != NULL );
//
// The source file is in the cache. Check whether we have
// associated a SSI_FILE with it.
//
pSsiFile = reinterpret_cast<SSI_FILE *>
(pOpenFile->QueryAssociatedObject());
if ( pSsiFile == NULL )
{
//
// Create new instance.
//
hr = SSI_FILE::CreateInstance( pOpenFile,
&pSsiFile );
if ( FAILED( hr ) )
{
goto failed;
}
DBG_ASSERT( pSsiFile != NULL );
//
// cache SsiFile
//
if ( !pSsiFile->AssociateWithFileCache() )
{
//
// If we failed to set Associated object it means there is one
// associated already. We can delete our copy and use that one
//
delete pSsiFile;
pSsiFile = reinterpret_cast<SSI_FILE *>
(pOpenFile->QueryAssociatedObject());
}
}
DBG_ASSERT( pSsiFile != NULL );
DBG_ASSERT( pSsiFile->CheckSignature() );
//
// We will keep the reference to cache entry (W3_FILE_INFO)
// because *ppSsiFile needs it
// caller must call DereferenceSsiFile() to assure proper cleanup
//
pOpenFile = NULL;
*ppSsiFile = pSsiFile;
return S_OK;
failed:
DBG_ASSERT ( FAILED( hr ) );
if ( pOpenFile != NULL )
{
pOpenFile->DereferenceCacheEntry();
pOpenFile = NULL;
}
*ppSsiFile = NULL;
return hr;
}
//static
HRESULT
SSI_FILE::CreateInstance(
IN W3_FILE_INFO * pOpenFile,
OUT SSI_FILE ** ppSsiFile
)
/*++
Routine Description:
Private routine used by GetReferencedInstance
It allocates and initializes SSI_FILE instance
Arguments:
ppSsiFile - newly created instance of SSI_FILE
Return Value:
HRESULT
--*/
{
HRESULT hr = E_FAIL;
SSI_FILE * pSsiFile = NULL;
pSsiFile = new SSI_FILE( pOpenFile );
if( pSsiFile == NULL )
{
hr = HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY );
goto failed;
}
hr = pSsiFile->Initialize();
if ( FAILED( hr ) )
{
goto failed;
}
*ppSsiFile = pSsiFile;
return S_OK;
failed:
DBG_ASSERT( FAILED( hr ) );
if ( pSsiFile != NULL )
{
delete pSsiFile;
pSsiFile = NULL;
}
*ppSsiFile = NULL;
return hr;
}
HRESULT
SSI_FILE::Initialize(
VOID
)
/*++
Routine Description:
Private routine
It initializes SSI_FILE instance
Arguments:
Return Value:
HRESULT
--*/
{
HRESULT hr = E_FAIL;
// File is memory mapped in this function only if
// W3core FILE CACHE doesn't cache the file in memory
//
if ( SSIGetFileData() == NULL )
{
if ( FAILED( hr = SSICreateFileMapping() ) )
{
goto failed;
}
}
//
// Build element list
//
hr = SSI_ELEMENT_LIST::CreateInstance(
this,
&_pSsiElementList );
if( FAILED( hr ) )
{
goto failed;
}
DBG_ASSERT( _pSsiElementList != NULL );
//
// Build headers that would be sent in the case there are no directives
// headers will be cached within SEL for optimized speed of stm files with no tags
//
if ( !_pSsiElementList->QueryHasDirectives() )
{
CHAR achNum[ SSI_MAX_NUMBER_STRING + 1 ];
DWORD cbFileSize = 0;
hr = SSIGetFileSize( &cbFileSize );
if( FAILED( hr ) )
{
goto failed;
}
_ultoa( cbFileSize,
achNum,
10 );
hr = AddToResponseHeaders( "Content-Length: " );
if ( FAILED( hr ) )
{
goto failed;
}
hr = AddToResponseHeaders( achNum );
if ( FAILED( hr ) )
{
goto failed;
}
hr = AddToResponseHeaders( "\r\n",
TRUE /*headers now include Content-Length*/);
if ( FAILED( hr ) )
{
goto failed;
}
CHAR * pszHeaderValue = SSIGetFileETag();
if ( pszHeaderValue != NULL )
{
hr = AddToResponseHeaders( "ETag: " );
if ( FAILED( hr ) )
{
goto failed;
}
hr = AddToResponseHeaders( pszHeaderValue );
if ( FAILED( hr ) )
{
goto failed;
}
hr = AddToResponseHeaders( "\r\n" );
if ( FAILED( hr ) )
{
goto failed;
}
}
pszHeaderValue = SSIGetLastModifiedString();
if ( pszHeaderValue != NULL )
{
hr = AddToResponseHeaders( "Last-Modified: " );
if ( FAILED( hr ) )
{
goto failed;
}
hr = AddToResponseHeaders( pszHeaderValue );
if ( FAILED( hr ) )
{
goto failed;
}
hr = AddToResponseHeaders( "\r\n" );
if ( FAILED( hr ) )
{
goto failed;
}
}
//
// Note: SSI_HEADER includes headers terminator!
//
AddToResponseHeaders( SSI_HEADER );
if ( FAILED( hr ) )
{
goto failed;
}
}
hr = S_OK;
goto finished;
failed:
DBG_ASSERT( FAILED( hr ) );
//
// Deletion of created data structures will be done in destructor
//
finished:
//
// Cleanup
//
// file mapping in memory was needed only to perform parsing
// it's time to delete it
// File is memory mapped in this function only if
// W3core FILE CACHE doesn't cache the file in memory
//
SSIDeleteFileMapping();
return hr;
}
HRESULT
SSI_FILE::SSICreateFileMapping(
VOID
)
/*++
Creates a mapping to a file
--*/
{
HANDLE hHandle;
HRESULT hr = E_FAIL;
DBG_ASSERT( _pFile != NULL );
//
// check for empty file
//
ULARGE_INTEGER Size;
_pFile->QuerySize( &Size );
if ( Size.LowPart == 0 && Size.HighPart == 0 )
{
//
// if file is empty then don't create mapping (it would fail with 1006)
//
_pvMappedBase = NULL;
return S_OK;
}
//
// file is not in cached in the memory by worker process
// Map it to memory now
//
hHandle = _pFile->QueryFileHandle();
if ( _hMapHandle != NULL )
{
if ( FAILED( hr = SSIDeleteFileMapping() ) )
{
goto failed;
}
}
_hMapHandle = ::CreateFileMapping( hHandle,
NULL,
PAGE_READONLY,
0,
0,
NULL );
if ( _hMapHandle == NULL )
{
DBGPRINTF(( DBG_CONTEXT,
"CreateFileMapping failed with %d\n",
GetLastError() ));
hr = HRESULT_FROM_WIN32( GetLastError() );
goto failed;
}
DBG_ASSERT ( _pvMappedBase == NULL );
_pvMappedBase = ::MapViewOfFile( _hMapHandle,
FILE_MAP_READ,
0,
0,
0 );
if ( _pvMappedBase == NULL )
{
DBGPRINTF(( DBG_CONTEXT,
"MapViewOfFile() failed with %d\n",
GetLastError() ));
hr = HRESULT_FROM_WIN32( GetLastError() );
goto failed;
}
return S_OK;
failed:
DBG_ASSERT( FAILED( hr ) );
return hr;
}
HRESULT
SSI_FILE::SSIDeleteFileMapping(
VOID
)
/*++
Closes mapping to a file
--*/
{
if ( _pvMappedBase != NULL )
{
::UnmapViewOfFile( _pvMappedBase );
_pvMappedBase = NULL;
}
if ( _hMapHandle != NULL )
{
::CloseHandle( _hMapHandle );
_hMapHandle = NULL;
}
return S_OK;
}
HRESULT
SSI_FILE::AddToResponseHeaders(
IN CHAR * pszHeaderChunk,
IN BOOL fIncludesContentLength
)
{
if ( fIncludesContentLength )
{
_fResponseHeadersIncludeContentLength = TRUE;
}
return _strResponseHeaders.Append( pszHeaderChunk );
}
HRESULT
SSI_FILE::GetResponseHeaders(
OUT CHAR ** ppszResponseHeaders,
OUT BOOL * pfIncludesContentLength
)
{
if ( _strResponseHeaders.IsEmpty() )
{
*ppszResponseHeaders = SSI_HEADER;
*pfIncludesContentLength = FALSE;
}
else
{
*ppszResponseHeaders = _strResponseHeaders.QueryStr();
*pfIncludesContentLength = _fResponseHeadersIncludeContentLength;
}
return S_OK;
}