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.

1191 lines
29 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name :
  4. urlinfo.cxx
  5. Abstract:
  6. Implementation of URL cache
  7. Author:
  8. Bilal Alam (balam) 8-Jan-2000
  9. Environment:
  10. Win32 - User Mode
  11. Project:
  12. ULW3.DLL
  13. --*/
  14. #include "precomp.hxx"
  15. //
  16. // This is the maximum size for a script map extension
  17. //
  18. #define MAX_EXT_LEN 128
  19. ALLOC_CACHE_HANDLER * W3_URL_INFO::sm_pachW3UrlInfo;
  20. DWORD W3_URL_INFO::sm_cMaxDots;
  21. HRESULT
  22. W3_URL_INFO_KEY::CreateCacheKey(
  23. WCHAR * pszKey,
  24. DWORD cchKey,
  25. DWORD cchSitePrefix,
  26. BOOL fCopy
  27. )
  28. /*++
  29. Description:
  30. Setup a URI cache key
  31. Arguments:
  32. pszKey - URL of cache key
  33. cchKey - size of URL
  34. cchSitePrefix - Size of site prefix ("LM/W3SVC/<n>")
  35. fCopy - Set to TRUE if we should copy the URL, else we just keep a ref
  36. Return:
  37. HRESULT
  38. --*/
  39. {
  40. HRESULT hr;
  41. _cchSitePrefix = cchSitePrefix;
  42. if ( fCopy )
  43. {
  44. hr = _strKey.Copy( pszKey );
  45. if ( FAILED( hr ) )
  46. {
  47. return hr;
  48. }
  49. _pszKey = _strKey.QueryStr();
  50. _cchKey = _strKey.QueryCCH();
  51. }
  52. else
  53. {
  54. _pszKey = pszKey;
  55. _cchKey = cchKey;
  56. }
  57. return NO_ERROR;
  58. }
  59. //static
  60. HRESULT
  61. W3_URL_INFO::Initialize(
  62. VOID
  63. )
  64. /*++
  65. Description:
  66. URI entry lookaside initialization
  67. Arguments:
  68. None
  69. Return:
  70. HRESULT
  71. --*/
  72. {
  73. ALLOC_CACHE_CONFIGURATION acConfig;
  74. HRESULT hr;
  75. DWORD dwError;
  76. HKEY hKey = NULL;
  77. DWORD cbData;
  78. DWORD dwType;
  79. DWORD dwValue;
  80. //
  81. // Default max dots is 100
  82. //
  83. sm_cMaxDots = 100;
  84. //
  85. // Look for a registry override to the max dot value
  86. // (one of the more useful configurable options in IIS)
  87. //
  88. dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  89. L"System\\CurrentControlSet\\Services\\inetinfo\\Parameters",
  90. 0,
  91. KEY_READ,
  92. &hKey );
  93. if ( dwError == ERROR_SUCCESS )
  94. {
  95. DBG_ASSERT( hKey != NULL );
  96. //
  97. // Should we be file caching at all?
  98. //
  99. cbData = sizeof( DWORD );
  100. dwError = RegQueryValueEx( hKey,
  101. L"MaxDepthDots",
  102. NULL,
  103. &dwType,
  104. (LPBYTE) &dwValue,
  105. &cbData );
  106. if ( dwError == ERROR_SUCCESS && dwType == REG_DWORD )
  107. {
  108. sm_cMaxDots = dwValue;
  109. }
  110. RegCloseKey( hKey );
  111. }
  112. //
  113. // Initialize allocation lookaside
  114. //
  115. acConfig.nConcurrency = 1;
  116. acConfig.nThreshold = 100;
  117. acConfig.cbSize = sizeof( W3_URL_INFO );
  118. DBG_ASSERT( sm_pachW3UrlInfo == NULL );
  119. sm_pachW3UrlInfo = new ALLOC_CACHE_HANDLER( "W3_URL_INFO",
  120. &acConfig );
  121. if ( sm_pachW3UrlInfo == NULL )
  122. {
  123. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  124. DBGPRINTF(( DBG_CONTEXT,
  125. "Error initializing sm_pachW3UrlInfo. hr = 0x%x\n",
  126. hr ));
  127. return hr;
  128. }
  129. return NO_ERROR;
  130. }
  131. //static
  132. VOID
  133. W3_URL_INFO::Terminate(
  134. VOID
  135. )
  136. /*++
  137. Description:
  138. URI cache cleanup
  139. Arguments:
  140. None
  141. Return:
  142. None
  143. --*/
  144. {
  145. if ( sm_pachW3UrlInfo != NULL )
  146. {
  147. delete sm_pachW3UrlInfo;
  148. sm_pachW3UrlInfo = NULL;
  149. }
  150. }
  151. HRESULT
  152. W3_URL_INFO::Create(
  153. STRU & strUrl,
  154. STRU & strMetadataPath
  155. )
  156. /*++
  157. Description:
  158. Initialize a W3_URL_INFO
  159. Arguments:
  160. strUrl - Url key for this entry
  161. strMetadataPath - Full metadata path used for key
  162. Return:
  163. HRESULT
  164. --*/
  165. {
  166. HRESULT hr;
  167. hr = _cacheKey.CreateCacheKey( strMetadataPath.QueryStr(),
  168. strMetadataPath.QueryCCH(),
  169. strMetadataPath.QueryCCH() - strUrl.QueryCCH(),
  170. TRUE );
  171. if ( FAILED( hr ) )
  172. {
  173. return hr;
  174. }
  175. //
  176. // Process the URL for execution info (and splitting path_info/url)
  177. //
  178. hr = ProcessUrl( strUrl );
  179. if( FAILED( hr ) )
  180. {
  181. return hr;
  182. }
  183. //
  184. // Now create physical path
  185. //
  186. hr = _pMetaData->BuildPhysicalPath( _strProcessedUrl,
  187. &_strPhysicalPath );
  188. if ( FAILED( hr ) )
  189. {
  190. return hr;
  191. }
  192. //
  193. // Now create translation of the whole URL. Note: this is not always
  194. // PathTranslated, so we do not name it so
  195. //
  196. hr = _pMetaData->BuildPhysicalPath( strUrl,
  197. &_strUrlTranslated );
  198. if ( FAILED( hr ) )
  199. {
  200. return hr;
  201. }
  202. return NO_ERROR;
  203. }
  204. HRESULT
  205. W3_URL_INFO::GetPathTranslated(
  206. W3_CONTEXT * pW3Context,
  207. BOOL fUsePathInfo,
  208. STRU * pstrPathTranslated
  209. )
  210. /*++
  211. Routine Description:
  212. Given the PATH_INFO of this cached entry, determine PATH_TRANSLATED.
  213. This involves:
  214. 1) Mapping PATH_INFO to a physical path
  215. 2) Calling filters if necessary for this path
  216. We cache the output of step 1)
  217. Arguments:
  218. pW3Context - W3Context
  219. pstrPathTranslated - Filled with physical path on success
  220. Return Value:
  221. HRESULT
  222. --*/
  223. {
  224. HRESULT hr = S_OK;
  225. W3_URL_INFO * pUrlInfo = NULL;
  226. BOOL fReadLocked = TRUE;
  227. if ( pW3Context == NULL ||
  228. pstrPathTranslated == NULL )
  229. {
  230. DBG_ASSERT( FALSE );
  231. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  232. }
  233. //
  234. // Check for cached path translated entry. If its there and flushed,
  235. // then release it (and try again)
  236. //
  237. if ( _pUrlInfoPathTranslated != NULL )
  238. {
  239. _PathTranslatedLock.ReadLock();
  240. if ( _pUrlInfoPathTranslated != NULL )
  241. {
  242. if ( _pUrlInfoPathTranslated->QueryIsFlushed() )
  243. {
  244. _PathTranslatedLock.ConvertSharedToExclusive();
  245. //
  246. // Remember to write unlock when we're done
  247. //
  248. fReadLocked = FALSE;
  249. //
  250. // Due to how lock works, someone may have freed it already
  251. //
  252. if ( _pUrlInfoPathTranslated != NULL )
  253. {
  254. _pUrlInfoPathTranslated->DereferenceCacheEntry();
  255. _pUrlInfoPathTranslated = NULL;
  256. }
  257. }
  258. else
  259. {
  260. _pUrlInfoPathTranslated->ReferenceCacheEntry();
  261. pUrlInfo = _pUrlInfoPathTranslated;
  262. }
  263. }
  264. if ( fReadLocked )
  265. {
  266. _PathTranslatedLock.ReadUnlock();
  267. }
  268. else
  269. {
  270. _PathTranslatedLock.WriteUnlock();
  271. }
  272. }
  273. //
  274. // Use the cached translated entry if there
  275. //
  276. if ( pUrlInfo == NULL )
  277. {
  278. //
  279. // Get and keep the metadata and urlinfo for this path
  280. //
  281. DBG_ASSERT( g_pW3Server->QueryUrlInfoCache() != NULL );
  282. if ( fUsePathInfo )
  283. {
  284. hr = g_pW3Server->QueryUrlInfoCache()->GetUrlInfo( pW3Context,
  285. _strPathInfo,
  286. &pUrlInfo );
  287. }
  288. else
  289. {
  290. pUrlInfo = this;
  291. pUrlInfo->ReferenceCacheEntry();
  292. }
  293. if ( FAILED( hr ) )
  294. {
  295. return hr;
  296. }
  297. DBG_ASSERT( pUrlInfo != NULL );
  298. //
  299. // Store away the URL info, provided it is an empty string.
  300. //
  301. // Basically, we're trying to optimize the case of "/foobar.dll".
  302. // In this case, path info is empty, and we want to avoid doing the
  303. // extra URL lookup for empty string.
  304. //
  305. if ( pUrlInfo->QueryCached() &&
  306. pUrlInfo->QueryUrl()[0] == L'\0' )
  307. {
  308. _PathTranslatedLock.WriteLock();
  309. if ( _pUrlInfoPathTranslated == NULL )
  310. {
  311. _pUrlInfoPathTranslated = pUrlInfo;
  312. }
  313. else
  314. {
  315. pUrlInfo->DereferenceCacheEntry();
  316. pUrlInfo = _pUrlInfoPathTranslated;
  317. }
  318. pUrlInfo->ReferenceCacheEntry();
  319. _PathTranslatedLock.WriteUnlock();
  320. }
  321. }
  322. DBG_ASSERT( pUrlInfo != NULL );
  323. //
  324. // Now call into the filter
  325. //
  326. hr = W3_STATE_URLINFO::FilterMapPath( pW3Context,
  327. pUrlInfo,
  328. pstrPathTranslated );
  329. pUrlInfo->DereferenceCacheEntry();
  330. return hr;
  331. }
  332. HRESULT
  333. W3_URL_INFO::GetFileInfo(
  334. CACHE_USER * pOpeningUser,
  335. BOOL fDoCache,
  336. W3_FILE_INFO ** ppFileInfo,
  337. FILE_CACHE_ASYNC_CONTEXT * pAsyncContext,
  338. BOOL * pfHandledSync,
  339. BOOL fAllowNoBuffering,
  340. BOOL fCheckForExistenceOnly
  341. )
  342. /*++
  343. Routine Description:
  344. Get file info associated with this cache entry. If it doesn't exist,
  345. then go to file cache directly to open the file
  346. Arguments:
  347. pOpeningUser - Opening user
  348. fDoCache - Should we cache the file
  349. ppFileInfo - Set to file cache entry on success
  350. pAsyncContext - In case an async read is desired, context with callback info rmation
  351. pfHandledSync - Did the open complete synchronously
  352. fAllowNoBuffering - If opening the file, can we open with no-OS-buffering
  353. fCheckFOrExistenceOnly - Check for existence only
  354. Return Value:
  355. HRESULT
  356. --*/
  357. {
  358. W3_FILE_INFO * pFileInfo = NULL;
  359. BOOL fCleanup = FALSE;
  360. HRESULT hr;
  361. if ( ppFileInfo == NULL ||
  362. pOpeningUser == NULL )
  363. {
  364. DBG_ASSERT( FALSE );
  365. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  366. }
  367. *ppFileInfo = NULL;
  368. //
  369. // Do we already have a file entry associated? If so use it, if it was
  370. // not already flushed
  371. //
  372. // BIG NOTE: We will also cache "existence-only" cache entries since
  373. // that makes our "Check-Path-Existence" option work fast.
  374. //
  375. // What that means is if we're not being asked for an existence-check,
  376. // then we need to not use an existence-only cache entry
  377. //
  378. LockCacheEntry();
  379. if ( _pFileInfo != NULL )
  380. {
  381. if ( _pFileInfo->Checkout(pOpeningUser) )
  382. {
  383. pFileInfo = _pFileInfo;
  384. }
  385. else
  386. {
  387. _pFileInfo->DereferenceCacheEntry();
  388. _pFileInfo = NULL;
  389. }
  390. }
  391. UnlockCacheEntry();
  392. //
  393. // Is this an existence-only entry? Is it usable
  394. //
  395. if ( pFileInfo != NULL &&
  396. pFileInfo->QueryFileHandle() == INVALID_HANDLE_VALUE &&
  397. pFileInfo->QueryFileBuffer() == NULL &&
  398. !fCheckForExistenceOnly )
  399. {
  400. //
  401. // Doh. This entry isn't very useful for us. Clear it out
  402. //
  403. LockCacheEntry();
  404. if ( _pFileInfo != NULL )
  405. {
  406. _pFileInfo = NULL;
  407. }
  408. UnlockCacheEntry();
  409. pFileInfo->DereferenceCacheEntry();
  410. pFileInfo = NULL;
  411. //
  412. // Fall thru and act as if we never had a cached file to begin with
  413. //
  414. }
  415. //
  416. // If we got a file entry, we're done, assuming access check is ok
  417. //
  418. if ( pFileInfo != NULL )
  419. {
  420. hr = pFileInfo->DoAccessCheck( pOpeningUser );
  421. if ( SUCCEEDED( hr ) )
  422. {
  423. *ppFileInfo = pFileInfo;
  424. if (pfHandledSync != NULL)
  425. {
  426. *pfHandledSync = TRUE;
  427. }
  428. return NO_ERROR;
  429. }
  430. else
  431. {
  432. pFileInfo->DereferenceCacheEntry();
  433. return hr;
  434. }
  435. }
  436. //
  437. // We'll have to go to file cache directly
  438. //
  439. DBG_ASSERT( g_pW3Server->QueryFileCache() != NULL );
  440. hr = g_pW3Server->QueryFileCache()->GetFileInfo(
  441. _strPhysicalPath,
  442. _pMetaData->QueryDirmonConfig(),
  443. pOpeningUser,
  444. fDoCache,
  445. &pFileInfo,
  446. pAsyncContext,
  447. pfHandledSync,
  448. fAllowNoBuffering,
  449. fCheckForExistenceOnly );
  450. if ( FAILED( hr ) )
  451. {
  452. return hr;
  453. }
  454. //
  455. // Now try to stuff the file descriptor into this object (bearing in mind
  456. // that another thread may try to do the same thing for this W3_URL_INFO)
  457. //
  458. if ( ((pfHandledSync == NULL) || *pfHandledSync) &&
  459. pFileInfo->QueryCached() )
  460. {
  461. LockCacheEntry();
  462. if ( _pFileInfo == NULL )
  463. {
  464. pFileInfo->ReferenceCacheEntry();
  465. _pFileInfo = pFileInfo;
  466. }
  467. UnlockCacheEntry();
  468. }
  469. //
  470. // It is OK if this thread was not able to associate the file entry.
  471. //
  472. *ppFileInfo = pFileInfo;
  473. return NO_ERROR;
  474. }
  475. HRESULT
  476. W3_URL_INFO::ProcessUrl(
  477. STRU & strUrl
  478. )
  479. /*++
  480. Routine Description:
  481. Process the URL and assocate execution information with
  482. the W3_URL_INFO.
  483. Called before adding a new URL info to the cache.
  484. Look through the url for script-mapped or executable extensions
  485. and update the W3_URL_INFO with the actual URL to execute,
  486. path-info, gateway type, etc.
  487. Arguments:
  488. strUrl - Original requested URL
  489. Return Value:
  490. SUCCEEDED()/FAILED()
  491. CODEWORK
  492. 1. Handle wildcard mappings
  493. --*/
  494. {
  495. HRESULT hr = NOERROR;
  496. DWORD cDots = 0;
  497. DBG_ASSERT( _pMetaData );
  498. STACK_STRU( strExtension, MAX_EXT_LEN );
  499. //
  500. // Reference the URL_INFO's data
  501. //
  502. STRU * pstrProcessedUrl = &_strProcessedUrl;
  503. STRU * pstrPathInfo = &_strPathInfo;
  504. //
  505. // Iterate over pstrProcessedUrl. These always point at the
  506. // pstrProcessedUrl string.
  507. //
  508. WCHAR * pszExtensionIter = NULL;
  509. WCHAR * pszPathInfoIter = NULL;
  510. //
  511. // Make a working copy of the URL, this will be modified if an
  512. // exectuable extension is found before the terminal node in the
  513. // path.
  514. //
  515. hr = pstrProcessedUrl->Copy( strUrl );
  516. if( FAILED(hr) )
  517. {
  518. goto failure;
  519. }
  520. //
  521. // Search the URL for an extension that matches something
  522. // we know how to execute.
  523. //
  524. pszExtensionIter = pstrProcessedUrl->QueryStr();
  525. while( pszExtensionIter = wcschr( pszExtensionIter, L'.' ) )
  526. {
  527. //
  528. // Maintain a count of the dots we encounter, any more than
  529. // sm_cMaxDots and we fail the request
  530. //
  531. cDots++;
  532. if ( cDots > sm_cMaxDots )
  533. {
  534. break;
  535. }
  536. //
  537. // Save the extension string
  538. //
  539. hr = strExtension.Copy( pszExtensionIter );
  540. if( FAILED(hr) )
  541. {
  542. goto failure;
  543. }
  544. //
  545. // Find the end of the string or the beginning of the path info
  546. //
  547. pszPathInfoIter = wcschr( pszExtensionIter, L'/' );
  548. if( pszPathInfoIter != NULL )
  549. {
  550. DBG_REQUIRE(
  551. strExtension.SetLen(
  552. DIFF(pszPathInfoIter - pszExtensionIter)
  553. )
  554. );
  555. }
  556. //
  557. // Lowercase the extension string to allow case-insensitive
  558. // comparisons.
  559. //
  560. _wcslwr( strExtension.QueryStr() );
  561. //
  562. // Try to find a matching script map entry
  563. //
  564. META_SCRIPT_MAP * pScriptMap;
  565. META_SCRIPT_MAP_ENTRY * pScriptMapEntry;
  566. pScriptMap = _pMetaData->QueryScriptMap();
  567. DBG_ASSERT( pScriptMap );
  568. if( pScriptMap->FindEntry( strExtension, &pScriptMapEntry ) )
  569. {
  570. DBG_ASSERT( pScriptMapEntry );
  571. _pScriptMapEntry = pScriptMapEntry;
  572. if( pszPathInfoIter )
  573. {
  574. hr = pstrPathInfo->Copy( pszPathInfoIter );
  575. if( FAILED(hr) )
  576. {
  577. goto failure;
  578. }
  579. //
  580. // Make sure that we truncate the URL so that we don't end
  581. // up downloading a source file by mistake.
  582. //
  583. DBG_REQUIRE(
  584. pstrProcessedUrl->SetLen(
  585. DIFF(pszPathInfoIter - pstrProcessedUrl->QueryStr())
  586. )
  587. );
  588. }
  589. else
  590. {
  591. pstrPathInfo->Reset();
  592. }
  593. //
  594. // Since we found the entry, we are done
  595. //
  596. break;
  597. }
  598. //
  599. // No matching script map, so check if this is a wellknown
  600. // Gateway type.
  601. //
  602. DBG_ASSERT( pScriptMapEntry == NULL );
  603. DBG_ASSERT( _pScriptMapEntry == NULL );
  604. //
  605. // Avoid all the string comps if we will never match.
  606. //
  607. if( strExtension.QueryCCH() == 4 )
  608. {
  609. GATEWAY_TYPE Gateway;
  610. Gateway = GATEWAY_UNKNOWN;
  611. //
  612. // Test extension against known gateway extensions.
  613. //
  614. // Does it make sense to allow the known extensions to be
  615. // configured?
  616. //
  617. if( wcscmp( L".dll", strExtension.QueryStr() ) == 0 ||
  618. wcscmp( L".isa", strExtension.QueryStr() ) == 0
  619. )
  620. {
  621. Gateway = GATEWAY_ISAPI;
  622. }
  623. else if( !wcscmp( L".exe", strExtension.QueryStr() ) ||
  624. !wcscmp( L".cgi", strExtension.QueryStr() ) ||
  625. !wcscmp( L".com", strExtension.QueryStr() ) )
  626. {
  627. Gateway = GATEWAY_CGI;
  628. }
  629. else if( wcscmp( L".map", strExtension.QueryStr() ) == 0 )
  630. {
  631. Gateway = GATEWAY_MAP;
  632. }
  633. //
  634. // OK. Before we continue, if the request was a GATEWAY_ISAPI
  635. // or GATEWAY_CGI and we do NOT have EXECUTE permissions, then
  636. // this really isn't a ISA/CGI after all
  637. //
  638. if ( Gateway == GATEWAY_CGI ||
  639. Gateway == GATEWAY_ISAPI )
  640. {
  641. if ( !( _pMetaData->QueryAccessPerms() & VROOT_MASK_EXECUTE ) )
  642. {
  643. Gateway = GATEWAY_UNKNOWN;
  644. }
  645. }
  646. //
  647. // The gateway is specified in the URL and we recognize it.
  648. //
  649. if( Gateway != GATEWAY_UNKNOWN )
  650. {
  651. _Gateway = Gateway;
  652. //
  653. // Save everthing after the matching extension as
  654. // path-info and truncate the URL so that it doesn't
  655. // include the path info
  656. //
  657. if( pszPathInfoIter )
  658. {
  659. hr = pstrPathInfo->Copy( pszPathInfoIter );
  660. if( FAILED(hr) )
  661. {
  662. goto failure;
  663. }
  664. DBG_REQUIRE(
  665. pstrProcessedUrl->SetLen(
  666. DIFF(pszPathInfoIter - pstrProcessedUrl->QueryStr())
  667. )
  668. );
  669. }
  670. else
  671. {
  672. pstrPathInfo->Reset();
  673. }
  674. //
  675. // We have a match so exit the loop
  676. //
  677. break;
  678. }
  679. }
  680. //
  681. // We do not have a matching entry, so continue to look for an
  682. // executable extension.
  683. //
  684. pszExtensionIter++;
  685. } // while (pszExtensionIter)
  686. //
  687. // Now associate the ContentType for this entry
  688. //
  689. if (_pScriptMapEntry == NULL &&
  690. _Gateway != GATEWAY_ISAPI &&
  691. _Gateway != GATEWAY_CGI)
  692. {
  693. if (FAILED(hr = SelectMimeMappingForFileExt(pstrProcessedUrl->QueryStr(),
  694. _pMetaData->QueryMimeMap(),
  695. &_strContentType,
  696. &_fDefaultMimeMap)))
  697. {
  698. goto failure;
  699. }
  700. }
  701. return S_OK;
  702. failure:
  703. DBG_ASSERT( FAILED(hr) );
  704. return FAILED(hr) ? hr : E_FAIL;
  705. }
  706. HRESULT
  707. W3_URL_INFO_CACHE::Initialize(
  708. VOID
  709. )
  710. /*++
  711. Description:
  712. Initialize URI cache
  713. Arguments:
  714. None
  715. Return:
  716. HRESULT
  717. --*/
  718. {
  719. HRESULT hr;
  720. DWORD dwData;
  721. DWORD dwType;
  722. DWORD cbData = sizeof( DWORD );
  723. DWORD csecTTL = DEFAULT_W3_URL_INFO_CACHE_TTL;
  724. HKEY hKey;
  725. //
  726. // What is the TTL for the URI cache
  727. //
  728. if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  729. L"System\\CurrentControlSet\\Services\\inetinfo\\Parameters",
  730. 0,
  731. KEY_READ,
  732. &hKey ) == ERROR_SUCCESS )
  733. {
  734. DBG_ASSERT( hKey != NULL );
  735. if ( RegQueryValueEx( hKey,
  736. L"ObjectCacheTTL",
  737. NULL,
  738. &dwType,
  739. (LPBYTE) &dwData,
  740. &cbData ) == ERROR_SUCCESS &&
  741. dwType == REG_DWORD )
  742. {
  743. csecTTL = dwData;
  744. }
  745. RegCloseKey( hKey );
  746. }
  747. //
  748. // We'll use TTL for scavenge period, and expect two inactive periods to
  749. // flush
  750. //
  751. hr = SetCacheConfiguration( csecTTL * 1000,
  752. csecTTL * 1000,
  753. CACHE_INVALIDATION_METADATA,
  754. NULL );
  755. if ( FAILED( hr ) )
  756. {
  757. return hr;
  758. }
  759. return W3_URL_INFO::Initialize();
  760. }
  761. HRESULT
  762. W3_URL_INFO_CACHE::GetUrlInfo(
  763. W3_CONTEXT * pW3Context,
  764. STRU & strUrl,
  765. W3_URL_INFO ** ppUrlInfo
  766. )
  767. /*++
  768. Routine Description:
  769. Retrieve a W3_URL_INFO, creating it if necessary
  770. Arguments:
  771. pW3Context - W3 context
  772. strUrl - Url to lookup
  773. ppUrlInfo - Filled with cache entry if successful
  774. Return Value:
  775. HRESULT
  776. --*/
  777. {
  778. W3_URL_INFO_KEY uriKey;
  779. W3_URL_INFO * pUrlInfo;
  780. STACK_STRU( strKey, MAX_PATH );
  781. STRU * pstrMBRoot;
  782. HRESULT hr;
  783. if ( pW3Context == NULL ||
  784. ppUrlInfo == NULL )
  785. {
  786. DBG_ASSERT( FALSE );
  787. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  788. }
  789. *ppUrlInfo = NULL;
  790. //
  791. // The key is the full metadata path. Start with the site prefix minus
  792. // the trailing '/'
  793. //
  794. pstrMBRoot = pW3Context->QuerySite()->QueryMBRoot();
  795. DBG_ASSERT( pstrMBRoot != NULL );
  796. hr = strKey.Copy( pstrMBRoot->QueryStr(),
  797. pstrMBRoot->QueryCCH() - 1 );
  798. if ( FAILED( hr ) )
  799. {
  800. return hr;
  801. }
  802. //
  803. // Now add the URL (note that some applications depend on
  804. // case-sensitivity in the URL.)
  805. //
  806. hr = strKey.Append( strUrl );
  807. if ( FAILED( hr ) )
  808. {
  809. return hr;
  810. }
  811. //
  812. // Setup a key to lookup
  813. //
  814. hr = uriKey.CreateCacheKey( strKey.QueryStr(),
  815. strKey.QueryCCH(),
  816. pW3Context->QuerySite()->QueryMBRoot()->QueryCCH(),
  817. FALSE );
  818. if ( FAILED( hr ) )
  819. {
  820. return hr;
  821. }
  822. //
  823. // Look it up
  824. //
  825. hr = FindCacheEntry( &uriKey,
  826. (CACHE_ENTRY**) &pUrlInfo );
  827. if ( SUCCEEDED( hr ) )
  828. {
  829. DBG_ASSERT( pUrlInfo != NULL );
  830. *ppUrlInfo = pUrlInfo;
  831. return NO_ERROR;
  832. }
  833. //
  834. // We need to create a URI cache entry
  835. //
  836. hr = CreateNewUrlInfo( pW3Context,
  837. strUrl,
  838. strKey,
  839. &pUrlInfo );
  840. if ( FAILED( hr ) )
  841. {
  842. return hr;
  843. }
  844. DBG_ASSERT( pUrlInfo != NULL );
  845. //
  846. // Add to the cache
  847. //
  848. AddCacheEntry( pUrlInfo );
  849. *ppUrlInfo = pUrlInfo;
  850. return NO_ERROR;
  851. }
  852. HRESULT
  853. W3_URL_INFO_CACHE::CreateNewUrlInfo(
  854. W3_CONTEXT * pW3Context,
  855. STRU & strUrl,
  856. STRU & strMetadataPath,
  857. W3_URL_INFO ** ppUrlInfo
  858. )
  859. /*++
  860. Routine Description:
  861. Create a new URI cache entry
  862. Arguments:
  863. pW3Context - Main context
  864. strUrl - Url
  865. strMetadataPath - Full metadata path used as key
  866. ppUrlInfo - Set to URI cache entry
  867. Return Value:
  868. HRESULT
  869. --*/
  870. {
  871. W3_METADATA * pMetaData = NULL;
  872. W3_URL_INFO * pUrlInfo = NULL;
  873. HRESULT hr;
  874. if ( pW3Context == NULL ||
  875. ppUrlInfo == NULL )
  876. {
  877. DBG_ASSERT( FALSE );
  878. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  879. }
  880. *ppUrlInfo = NULL;
  881. //
  882. // Find a metacache entry
  883. //
  884. DBG_ASSERT( g_pW3Server->QueryMetaCache() != NULL );
  885. hr = g_pW3Server->QueryMetaCache()->GetMetaData( pW3Context,
  886. strUrl,
  887. &pMetaData );
  888. if ( FAILED( hr ) )
  889. {
  890. //
  891. // Check for RPC disconnected error. If that's what happened,
  892. // mark unhealthy.
  893. //
  894. // Oh yeah, make sure we only log one message
  895. //
  896. if ( hr == RPC_E_DISCONNECTED ||
  897. hr == HRESULT_FROM_WIN32( RPC_S_SERVER_UNAVAILABLE ) ||
  898. hr == HRESULT_FROM_WIN32( RPC_S_CALL_FAILED ) ||
  899. hr == HRESULT_FROM_WIN32( RPC_S_CALL_FAILED_DNE ) )
  900. {
  901. if ( InterlockedExchange( (LPLONG) &_fMarkedUnhealthy, TRUE ) == FALSE )
  902. {
  903. g_pW3Server->LogEvent( W3_EVENT_NO_METABASE,
  904. 0,
  905. NULL,
  906. 0 );
  907. UlAtqSetUnhealthy();
  908. }
  909. }
  910. return hr;
  911. }
  912. DBG_ASSERT( pMetaData != NULL );
  913. //
  914. // Create a W3_URL_INFO
  915. //
  916. pUrlInfo = new W3_URL_INFO( this, pMetaData );
  917. if ( pUrlInfo == NULL )
  918. {
  919. hr = HRESULT_FROM_WIN32( GetLastError() );
  920. pMetaData->DereferenceCacheEntry();
  921. return hr;
  922. }
  923. hr = pUrlInfo->Create( strUrl,
  924. strMetadataPath );
  925. if ( FAILED( hr ) )
  926. {
  927. pUrlInfo->DereferenceCacheEntry();
  928. return hr;
  929. }
  930. *ppUrlInfo = pUrlInfo;
  931. return NO_ERROR;
  932. }