Response caching in IIS7

IIS caches everything it can to save CPU cycles wherever possible. IIS6 had user-mode file cache, token cache, URI cache, metadata cache and kernel-mode http.sys response cache. These caches are mostly unchanged in IIS 7.0 other than following changes I can think of.

  1. Static compression module disable kernel caching of response if static compression is enabled for the request but the client requested uncompressed response. This makes sure only compressed response is cached in kernel.
  2. You might see few changes in performance counters because URI cache module maintains additional pointers to cached files and metadata objects which saves some hashtable lookups.

Native output cache is the new user mode response cache added in IIS7. This module provides similar functionality as provided by the managed output cache module in ASP.NET. Functionality of this module can be controlled by editing system.webServer/caching section or by using IHttpCachePolicy intrinsic. Following properties can be set in system.webServer/caching section.

  1. enabled – This property tells if output caching is enabled or not for this URL. If disabled, output cache module won’t do anything in ResolveRequestCache and UpdateRequestCache stage. Setting enabled to true doesn’t ensure response caching. Some module must set user cache policy.
  2. enableKernelCache – Controls if kernel caching is enabled or not for this URL. Output cache module calls IHttpResponse::DisableKernelCache if this property is set to false. Output cache module does kernel caching work in SendResponse stage if no one called DisableKernelCache in the pipeline. Setting enableKernelCache to true doesn’t ensure kernel caching of response. Some module must set the kernel cache policy.
  3. maxCacheSize – Maximum size of output cache in MB. Value 0 means max cache size is calculated automatically. We use half of available physical memory or available virtual memory which ever is less.
  4. maxResponseSize – Maximum size of the response in bytes that can be stored in output cache. 0 means no limit.

Although you can set maxCacheSize, maxResponseSize for a URL, output cache module uses values set at root level only. If we have per apppool properties in future, these will be configurable for each application pool. If output cache is enabled, you can control its behavior for different file types by adding profiles for different file extensions. These profiles make output cache module populate IHttpCachePolicy intrinsic which enables user/kernel caching of response. Properties which can be set in a profile are similar to ones available for system.web/caching/outputCacheSettings profiles. Following properties are allowed for system.webServer/caching profiles:

  1. extension – E.g. “.asp”, “.htm” etc. * is used as wildcard entry. If profile for a particular extension is not found, profile for extension * will be used if present.
  2. policy – can be DontCache | CacheUntilChange | CacheForTimePeriod | DisableCache (only in server). Output cache module changes IHttpCachePolicy intrinsic depending on value of this property. DontCache means that intrinsic is not set but that doesn’t prevent other modules from setting it and enable caching. In server we have added DisableCache option which makes sure that response is not cached even if some other module sets the policy telling output cache module to cache the response.
  3. kernelCachePolicy – can be DontCache | CacheUntilChange | CacheForTimePeriod | DisableCache (only in server). As above, DontCache doesn’t prevent other modules from setting kernel cache policy. For static files, static file handler sets kernel cache policy which enable kernel caching of the response. In server DisableCache option makes sure that response doesn’t get cached in kernel.
  4. duration – duration property is only used when policy or kernelCachePolicy is set to CacheForTimePeriod.
  5. location – sets cache-control response header for client caching. Cache-control response header is set depending on value of this property as following.
    Any | Downstream – public
    ServerAndClient | Client – private
    None | Server – no-cache
  6. varyByHeaders – comma separated list of request headers. Multiple responses to requests having different values of these headers will be stored in the cache. You might be returning different responses based on Accept-Language or User-Agent or Accept-Encoding header. All the responses will get cached in memory.
  7. varyByQueryString – comma separated query string variables. Multiple responses get cached if query string variable values are different in different requests. In server you can set varyByQueryString to "*" (star) which makes output cache module to cache a separate response if any of the query string variable value is different.

varyBy and location headers are used by user mode cache only. These properties have no effect on kernel caching. So if policy is set to DontCache, these properties are not used. To make output cache module cache multiple responses by an asp page for 30 minutes which returns different responses based on value of querystring variable “action” and also based on request header “User-agent”, caching section will look like

 <profiles enabled=”true”>
    <add extension=”.asp” policy=”CacheForTimePeriod” duration=”00:30:00” varyByQueryString=”action” varyByHeaders=”User-Agent”/>

Output cache module populates the IHttpCachePolicy intrinsic in BeginRequest stage if a matching profile is found. Other modules can still change cache policy for the current request which might change user-mode or kernel mode caching behavior. Output cache caches 200 responses to GET requests only. If some module already flushed the response by the time request reaches UpdateRequestCache stage or if headers are suppressed, response is not cached in output cache module. Output cache module only caches the response if some other module hasn’t already cached it indicated by IHttpCachePolicy::SetIsCached. Also caching happens only for frequently hit content. Definition of frequently hit content is controlled by frequentHitThreshold and frequentHitTimePeriod properties defined in system.webServer/serverRuntime section. Default values define frequently hit content as ones which are requested twice in 10 seconds.

More details on IHttpCachePolicy coming in a future post.


Feature delegation of custom module in UI

I was at TechEd developers last week and one question that came up was what it takes for custom modules to show up in "Features Delegation" UI. I had image copyright walkthrough setup on the machine and in spite of having the custom schema xml and <section> entry in applicationHost.config, imageCopyright was not shown in "Features Delegation" UI. I did some more investigations on it and following is what it takes for UI to offer delegation to a custom module.

As feature delegation can be done at various levels, just having a new section doesn’t allow UI to offer delegation for it. It requires one to extend admin UI tool. Go through image copyright walkthrough to see how to extend UI. Following methods need to be implemented in the UI module provider for feature delegation page to include your custom module as well.

  1. public bool SupportsDelegation – Feature delegation UI checks if the custom module provider supports delegation using this property. Abstract class ModuleProvider which is base class of all module providers returns false by default. If you dont override this property in your custom module provider, your feature wont show up in the "features delegation" list.
  2. public DelegationState GetChildDelegationState(string path) – Features delegation UI call this method to check the current delegation state of a module at a given path.
  3. public DelegationState[] GetSupportedChildDelegationStates(string path) – Features delegation UI calls this to get the supported delegation states. For configuration sections this would typically return Read Only, Read/Write, Remove delegation and Reset to inherited.
  4. public void SetChildDelegationState(string path, DelegationState delegationState) – This gets called when someone changes the delegation of the module in UI. delegationState is the new state which should be set at path ‘path’.

Implementing all this for image copyright module is easy. Instead of deriving imageCopyrightUIProvider class from ModuleProvider, we can derive it from ConfigurationModuleProvider which has the above methods implemented in it. ConfigurationModuleProvider requires you to add a get property to your module provider to tell the name of the section which gets locked. For image copyright walkthrough following changes are required.

// Inherit from ConfigurationModuleProvider instead of ModuleProvider
class imageCopyrightUIProvider : ConfigurationModuleProvider

// Add ConfigurationSectionName getter to tell section name which gets locked
override string ConfigurationSectionName
        return "system.webServer/imageCopyright";

Compile your module again, add to GAC and launch UI again. You should see imageCopyRight in your features delegation UI. I tried locking the section at server level by selecting "Read Only" from the available options and sure enough UI added the following to applicationHost.config which locked the section at machine level.

<location path="" overrideMode="Deny">
        <imageCopyright />