|
|
/*++
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; }
|