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

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. ssi_file.hxx
  5. Abstract:
  6. This module contains the server side include processing code. We
  7. aim for support as specified by iis\spec\ssi.doc. The code is based
  8. on existing SSI support done in iis\svcs\w3\gateways\ssinc\ssinc.cxx.
  9. SSI_FILE class handles all the details file access.
  10. File cache of W3CORE is leveraged to cache STM files
  11. ( using W3CORE file cache directly doesn't necessarily conform
  12. with ISAPI rules because we use private hooks not officially
  13. exposed to ISAPIs )
  14. Author:
  15. Ming Lu (MingLu) 5-Apr-2000
  16. Revision history
  17. Jaroslad Dec-2000
  18. - modified to execute asynchronously
  19. Jaroslad Apr-2001
  20. - added VectorSend support, keepalive, split to multiple source files
  21. --*/
  22. #include "precomp.hxx"
  23. //
  24. // access to W3CORE cache
  25. //
  26. //static
  27. W3_FILE_INFO_CACHE * SSI_FILE::s_pFileCache = NULL;
  28. //static
  29. HRESULT
  30. SSI_FILE::InitializeGlobals(
  31. VOID
  32. )
  33. /*++
  34. Routine Description:
  35. Initialization of globals
  36. Arguments:
  37. Return Value:
  38. HRESULT
  39. --*/
  40. {
  41. //
  42. // Get the cache instance for W3CORE.DLL
  43. //
  44. s_pFileCache = W3_FILE_INFO_CACHE::GetFileCache();
  45. if ( s_pFileCache == NULL )
  46. {
  47. return HRESULT_FROM_WIN32( ERROR_NOT_FOUND );
  48. }
  49. return S_OK;
  50. }
  51. //static
  52. VOID
  53. SSI_FILE::TerminateGlobals(
  54. VOID
  55. )
  56. /*++
  57. Routine Description:
  58. Cleanup of globals
  59. Arguments:
  60. Return Value:
  61. VOID
  62. --*/
  63. {
  64. //
  65. // ssinc.dll is using file cache from w3core
  66. // All objects associated with file cache must be cleaned up
  67. // before letting to unload this DLL
  68. //
  69. W3CacheFlushAllCaches();
  70. }
  71. SSI_FILE::SSI_FILE(
  72. IN W3_FILE_INFO * pOpenFile
  73. ) :
  74. _hMapHandle ( NULL ),
  75. _pvMappedBase ( NULL ),
  76. _fResponseHeadersIncludeContentLength( FALSE ),
  77. _strResponseHeaders( _abResponseHeaders, sizeof(_abResponseHeaders) ),
  78. _pFile ( pOpenFile )
  79. {
  80. DBG_ASSERT( _pFile != NULL );
  81. _dwSignature = SSI_FILE_SIGNATURE;
  82. }
  83. SSI_FILE::~SSI_FILE()
  84. {
  85. DBG_ASSERT( CheckSignature() );
  86. _dwSignature = SSI_FILE_SIGNATURE_FREED;
  87. //
  88. // delete File Memory Mapping if still happens to be around
  89. //
  90. SSIDeleteFileMapping();
  91. //
  92. // reset backpointer to W3_FILE_INFO,
  93. // no cleanup is needed, because W3_FILE_INFO actually
  94. // controls the lifetime of SSI_FILE (through the Cleanup() call)
  95. // once SSI_FILE instance is associated with W3_FILE_INFO
  96. //
  97. _pFile = NULL;
  98. if ( _pSsiElementList != NULL )
  99. {
  100. delete _pSsiElementList;
  101. _pSsiElementList = NULL;
  102. }
  103. }
  104. //static
  105. HRESULT
  106. SSI_FILE::GetReferencedInstance(
  107. IN STRU& strFilename,
  108. HANDLE hUserToken,
  109. OUT SSI_FILE ** ppSsiFile
  110. )
  111. /*++
  112. Routine Description:
  113. Lookup SSI_FILE
  114. It builds the Server Side Include Element List the first
  115. time a .stm file is sent. Subsequently, it is
  116. checked out from the associated cache blob.
  117. SSI_FILE is refcounted indirectly by association with the
  118. W3_FILE_INFO
  119. Once GetReferencedInstance() returns successfully then the
  120. DereferenceSsiFile() must be called to assure proper cleanup
  121. Arguments:
  122. Return Value:
  123. HRESULT
  124. --*/
  125. {
  126. HRESULT hr = E_FAIL;
  127. CACHE_USER fileUser;
  128. SSI_FILE * pSsiFile = NULL;
  129. W3_FILE_INFO * pOpenFile = NULL;
  130. DBG_ASSERT( ppSsiFile != NULL );
  131. fileUser._hToken = hUserToken;
  132. hr = s_pFileCache->GetFileInfo( strFilename,
  133. NULL,
  134. &fileUser,
  135. TRUE,
  136. &pOpenFile );
  137. if ( FAILED( hr ) )
  138. {
  139. goto failed;
  140. }
  141. DBG_ASSERT( pOpenFile != NULL );
  142. //
  143. // The source file is in the cache. Check whether we have
  144. // associated a SSI_FILE with it.
  145. //
  146. pSsiFile = reinterpret_cast<SSI_FILE *>
  147. (pOpenFile->QueryAssociatedObject());
  148. if ( pSsiFile == NULL )
  149. {
  150. //
  151. // Create new instance.
  152. //
  153. hr = SSI_FILE::CreateInstance( pOpenFile,
  154. &pSsiFile );
  155. if ( FAILED( hr ) )
  156. {
  157. goto failed;
  158. }
  159. DBG_ASSERT( pSsiFile != NULL );
  160. //
  161. // cache SsiFile
  162. //
  163. if ( !pSsiFile->AssociateWithFileCache() )
  164. {
  165. //
  166. // If we failed to set Associated object it means there is one
  167. // associated already. We can delete our copy and use that one
  168. //
  169. delete pSsiFile;
  170. pSsiFile = reinterpret_cast<SSI_FILE *>
  171. (pOpenFile->QueryAssociatedObject());
  172. }
  173. }
  174. DBG_ASSERT( pSsiFile != NULL );
  175. DBG_ASSERT( pSsiFile->CheckSignature() );
  176. //
  177. // We will keep the reference to cache entry (W3_FILE_INFO)
  178. // because *ppSsiFile needs it
  179. // caller must call DereferenceSsiFile() to assure proper cleanup
  180. //
  181. pOpenFile = NULL;
  182. *ppSsiFile = pSsiFile;
  183. return S_OK;
  184. failed:
  185. DBG_ASSERT ( FAILED( hr ) );
  186. if ( pOpenFile != NULL )
  187. {
  188. pOpenFile->DereferenceCacheEntry();
  189. pOpenFile = NULL;
  190. }
  191. *ppSsiFile = NULL;
  192. return hr;
  193. }
  194. //static
  195. HRESULT
  196. SSI_FILE::CreateInstance(
  197. IN W3_FILE_INFO * pOpenFile,
  198. OUT SSI_FILE ** ppSsiFile
  199. )
  200. /*++
  201. Routine Description:
  202. Private routine used by GetReferencedInstance
  203. It allocates and initializes SSI_FILE instance
  204. Arguments:
  205. ppSsiFile - newly created instance of SSI_FILE
  206. Return Value:
  207. HRESULT
  208. --*/
  209. {
  210. HRESULT hr = E_FAIL;
  211. SSI_FILE * pSsiFile = NULL;
  212. pSsiFile = new SSI_FILE( pOpenFile );
  213. if( pSsiFile == NULL )
  214. {
  215. hr = HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY );
  216. goto failed;
  217. }
  218. hr = pSsiFile->Initialize();
  219. if ( FAILED( hr ) )
  220. {
  221. goto failed;
  222. }
  223. *ppSsiFile = pSsiFile;
  224. return S_OK;
  225. failed:
  226. DBG_ASSERT( FAILED( hr ) );
  227. if ( pSsiFile != NULL )
  228. {
  229. delete pSsiFile;
  230. pSsiFile = NULL;
  231. }
  232. *ppSsiFile = NULL;
  233. return hr;
  234. }
  235. HRESULT
  236. SSI_FILE::Initialize(
  237. VOID
  238. )
  239. /*++
  240. Routine Description:
  241. Private routine
  242. It initializes SSI_FILE instance
  243. Arguments:
  244. Return Value:
  245. HRESULT
  246. --*/
  247. {
  248. HRESULT hr = E_FAIL;
  249. // File is memory mapped in this function only if
  250. // W3core FILE CACHE doesn't cache the file in memory
  251. //
  252. if ( SSIGetFileData() == NULL )
  253. {
  254. if ( FAILED( hr = SSICreateFileMapping() ) )
  255. {
  256. goto failed;
  257. }
  258. }
  259. //
  260. // Build element list
  261. //
  262. hr = SSI_ELEMENT_LIST::CreateInstance(
  263. this,
  264. &_pSsiElementList );
  265. if( FAILED( hr ) )
  266. {
  267. goto failed;
  268. }
  269. DBG_ASSERT( _pSsiElementList != NULL );
  270. //
  271. // Build headers that would be sent in the case there are no directives
  272. // headers will be cached within SEL for optimized speed of stm files with no tags
  273. //
  274. if ( !_pSsiElementList->QueryHasDirectives() )
  275. {
  276. CHAR achNum[ SSI_MAX_NUMBER_STRING + 1 ];
  277. DWORD cbFileSize = 0;
  278. hr = SSIGetFileSize( &cbFileSize );
  279. if( FAILED( hr ) )
  280. {
  281. goto failed;
  282. }
  283. _ultoa( cbFileSize,
  284. achNum,
  285. 10 );
  286. hr = AddToResponseHeaders( "Content-Length: " );
  287. if ( FAILED( hr ) )
  288. {
  289. goto failed;
  290. }
  291. hr = AddToResponseHeaders( achNum );
  292. if ( FAILED( hr ) )
  293. {
  294. goto failed;
  295. }
  296. hr = AddToResponseHeaders( "\r\n",
  297. TRUE /*headers now include Content-Length*/);
  298. if ( FAILED( hr ) )
  299. {
  300. goto failed;
  301. }
  302. CHAR * pszHeaderValue = SSIGetFileETag();
  303. if ( pszHeaderValue != NULL )
  304. {
  305. hr = AddToResponseHeaders( "ETag: " );
  306. if ( FAILED( hr ) )
  307. {
  308. goto failed;
  309. }
  310. hr = AddToResponseHeaders( pszHeaderValue );
  311. if ( FAILED( hr ) )
  312. {
  313. goto failed;
  314. }
  315. hr = AddToResponseHeaders( "\r\n" );
  316. if ( FAILED( hr ) )
  317. {
  318. goto failed;
  319. }
  320. }
  321. pszHeaderValue = SSIGetLastModifiedString();
  322. if ( pszHeaderValue != NULL )
  323. {
  324. hr = AddToResponseHeaders( "Last-Modified: " );
  325. if ( FAILED( hr ) )
  326. {
  327. goto failed;
  328. }
  329. hr = AddToResponseHeaders( pszHeaderValue );
  330. if ( FAILED( hr ) )
  331. {
  332. goto failed;
  333. }
  334. hr = AddToResponseHeaders( "\r\n" );
  335. if ( FAILED( hr ) )
  336. {
  337. goto failed;
  338. }
  339. }
  340. //
  341. // Note: SSI_HEADER includes headers terminator!
  342. //
  343. AddToResponseHeaders( SSI_HEADER );
  344. if ( FAILED( hr ) )
  345. {
  346. goto failed;
  347. }
  348. }
  349. hr = S_OK;
  350. goto finished;
  351. failed:
  352. DBG_ASSERT( FAILED( hr ) );
  353. //
  354. // Deletion of created data structures will be done in destructor
  355. //
  356. finished:
  357. //
  358. // Cleanup
  359. //
  360. // file mapping in memory was needed only to perform parsing
  361. // it's time to delete it
  362. // File is memory mapped in this function only if
  363. // W3core FILE CACHE doesn't cache the file in memory
  364. //
  365. SSIDeleteFileMapping();
  366. return hr;
  367. }
  368. HRESULT
  369. SSI_FILE::SSICreateFileMapping(
  370. VOID
  371. )
  372. /*++
  373. Creates a mapping to a file
  374. --*/
  375. {
  376. HANDLE hHandle;
  377. HRESULT hr = E_FAIL;
  378. DBG_ASSERT( _pFile != NULL );
  379. //
  380. // check for empty file
  381. //
  382. ULARGE_INTEGER Size;
  383. _pFile->QuerySize( &Size );
  384. if ( Size.LowPart == 0 && Size.HighPart == 0 )
  385. {
  386. //
  387. // if file is empty then don't create mapping (it would fail with 1006)
  388. //
  389. _pvMappedBase = NULL;
  390. return S_OK;
  391. }
  392. //
  393. // file is not in cached in the memory by worker process
  394. // Map it to memory now
  395. //
  396. hHandle = _pFile->QueryFileHandle();
  397. if ( _hMapHandle != NULL )
  398. {
  399. if ( FAILED( hr = SSIDeleteFileMapping() ) )
  400. {
  401. goto failed;
  402. }
  403. }
  404. _hMapHandle = ::CreateFileMapping( hHandle,
  405. NULL,
  406. PAGE_READONLY,
  407. 0,
  408. 0,
  409. NULL );
  410. if ( _hMapHandle == NULL )
  411. {
  412. DBGPRINTF(( DBG_CONTEXT,
  413. "CreateFileMapping failed with %d\n",
  414. GetLastError() ));
  415. hr = HRESULT_FROM_WIN32( GetLastError() );
  416. goto failed;
  417. }
  418. DBG_ASSERT ( _pvMappedBase == NULL );
  419. _pvMappedBase = ::MapViewOfFile( _hMapHandle,
  420. FILE_MAP_READ,
  421. 0,
  422. 0,
  423. 0 );
  424. if ( _pvMappedBase == NULL )
  425. {
  426. DBGPRINTF(( DBG_CONTEXT,
  427. "MapViewOfFile() failed with %d\n",
  428. GetLastError() ));
  429. hr = HRESULT_FROM_WIN32( GetLastError() );
  430. goto failed;
  431. }
  432. return S_OK;
  433. failed:
  434. DBG_ASSERT( FAILED( hr ) );
  435. return hr;
  436. }
  437. HRESULT
  438. SSI_FILE::SSIDeleteFileMapping(
  439. VOID
  440. )
  441. /*++
  442. Closes mapping to a file
  443. --*/
  444. {
  445. if ( _pvMappedBase != NULL )
  446. {
  447. ::UnmapViewOfFile( _pvMappedBase );
  448. _pvMappedBase = NULL;
  449. }
  450. if ( _hMapHandle != NULL )
  451. {
  452. ::CloseHandle( _hMapHandle );
  453. _hMapHandle = NULL;
  454. }
  455. return S_OK;
  456. }
  457. HRESULT
  458. SSI_FILE::AddToResponseHeaders(
  459. IN CHAR * pszHeaderChunk,
  460. IN BOOL fIncludesContentLength
  461. )
  462. {
  463. if ( fIncludesContentLength )
  464. {
  465. _fResponseHeadersIncludeContentLength = TRUE;
  466. }
  467. return _strResponseHeaders.Append( pszHeaderChunk );
  468. }
  469. HRESULT
  470. SSI_FILE::GetResponseHeaders(
  471. OUT CHAR ** ppszResponseHeaders,
  472. OUT BOOL * pfIncludesContentLength
  473. )
  474. {
  475. if ( _strResponseHeaders.IsEmpty() )
  476. {
  477. *ppszResponseHeaders = SSI_HEADER;
  478. *pfIncludesContentLength = FALSE;
  479. }
  480. else
  481. {
  482. *ppszResponseHeaders = _strResponseHeaders.QueryStr();
  483. *pfIncludesContentLength = _fResponseHeadersIncludeContentLength;
  484. }
  485. return S_OK;
  486. }