Galin Iliev's blog

Software Architecture & Development

Breaking changes in IIS7 (integrated pipeline) when hosting ASP.NET 2.0 sites

Mike Volodarsky posted a nice list of breaking changes when you run ASP.NET 2.0 web sites on IIS7 Integrated mode.

Here is the list (for workarounds take a look at Mike's blog post).

Migration errors
  1. ASP.NET applications require migration when specifying configuration in <httpModules> or <httpHandlers>.
  2. ASP.NET applications produce a warning when the application enables request impersonation by specifying <identity impersonate=”true”> in configuration.
  3. You receive a configuration error when your application configuration includes an encrypted <identity> section.
Authentication, Authorization, and Impersonation

  1. Applications cannot simultaneously use FormsAuthentication and WindowsAuthentication.
  2. Windows Authentication is performed in the kernel by default.  This may cause HTTP clients that send credentials on the initial request to fail.
  3. Passport authentication is not supported.
  4. HttpRequest.LogonUserIdentity throws an InvalidOperationException when accessed in a module before PostAuthenticateRequest.
  5. Client impersonation is not applied in a module in the BeginRequest and AuthenticateRequest stages.
  6. Defining an DefaultAuthentication_OnAuthenticate method in global.asax throws PlatformNotSupportedException.
  7. Applications that implement WindowsAuthentication_OnAuthenticate in global.asax will not be notified when the request is anonymous.
Request limits and URL processing
  1. Request URLs containing unencoded “+” characters in the path (not querystring) is rejected by default.
  2. Requests with querystrings larger then 2048 bytes will be rejected by default.
Changes in response header processing
  1. IIS always rejects new lines in response headers (even if ASP.NET enableHeaderChecking is set to false)When the response headers are cleared with HttpResponse.ClearHeaders, default ASP.NET headers are not generated.  This may result in the lack of Cache-Control: private header that prevents the caching of the response on the client.
  2. When the response is empty, the Content-Type header is not suppressed.
  3. When the response headers are cleared with HttpResponse.ClearHeaders, default ASP.NET headers are not generated.  This may result in the lack of Cache-Control: private header that prevents the caching of the response on the client.
Changes in application and module event processing
  1. It is not possible to access the request through the HttpContext.Current property in Application_Start in global.asax.
  2. The order in which module event handlers execute may be different then in Classic mode.
  3. ASP.NET modules in early request processing stages will see requests that previously may have been rejected by IIS prior to entering ASP.NET.  This includes modules running in BeginRequest seeing anonymous requests for resources that require authentication.
Other application changes
  1. DefaultHttpHandler is not supported.  Applications relying on sub-classes of DefaultHttpHandler will not be able to serve requests.
    It is possible to write to the response after an exception has occurred.
  2. It is not possible to use the ClearError API to prevent an exception from being written to the response if the exception has occurred in a prior pipeline stage.
  3. HttpResponse.AppendToLog does not automatically prepend the querystring to the URL.
Other changes
  1. ASP.NET threading settings are not used to control the request concurrency in Integrated mode.
  2. ASP.NET application queues are not used in Integrated mode.  Therefore, the “ASP.NET Applications\Requests in Application Queue” performance counter will always have a value of 0
  3. IIS 7.0 always restarts ASP.NET applications when changes are made to the application’s root web.config file.  Because of this, waitChangeNotification and maxWaitChangeNotification attributes have no effect.

IIS7: How to set cache control for static content?

Caching is popular technique for reducing network traffic and server recourses when it comes to web content. But how we can cache static content like .jpg, gif, .js files?! 

Seems easy: just start IIS Manager... and ... Wait! there is no icon for it there! Hmm

it's not so easy though... but possible:

just follow these steps:

1. Allow overriding static content setting:

open %systemroot%\System32\inetsrv\config\applicationHost.config
search for <section name="staticContent" overrideModeDefault="Deny" />

change it to <section name="staticContent" overrideModeDefault="Allow" />


2. set cache settings using following commands (from IIS.NET forums)

set max-age to 1hr for all static files under /images on default-web-site, run the following


\Windows\system32\inetsrv\appcmd.exe set config "Default Web Site/images" -section:system.webServer/staticContent -clientCache.cacheControlMode:UseMaxAge


\Windows\system32\inetsrv\appcmd.exe set config "Default Web Site/images" -section:system.webServer/staticContent -clientCache.cacheControlMaxAge:"01:00:00"


If you give it a shot now you will see that the content is cached! Not believe?! Try to request image from the browser, overwrite the image with image with same name and different content and request image from the browser again...

But the bad news is that caching is on the server - so you haven't reduced network traffic as web server is still hit.


3. Cache it on client

open %systemroot%\System32\inetsrv\config\applicationHost.config

and change the lines like this


<location path="MyWebsite"> 
                <add extension=".html" policy="CacheUntilChange" kernelCachePolicy="DontCache" location="Client" /> 
                <add extension=".htm" policy="CacheUntilChange" kernelCachePolicy="DontCache" location="Client" /> 
                <add extension=".gif" policy="CacheUntilChange" kernelCachePolicy="DontCache" location="Client" /> 
                <add extension=".js" policy="CacheUntilChange" kernelCachePolicy="DontCache" location="Client" /> 
                <add extension=".css" policy="CacheUntilChange" kernelCachePolicy="DontCache" location="Client" /> 
                <add extension=".jpg" policy="CacheUntilChange" kernelCachePolicy="DontCache" location="Client" /> 
                <add extension=".jpeg" policy="CacheUntilChange" kernelCachePolicy="DontCache" location="Client" /> 
                <add extension=".zip" policy="CacheUntilChange" kernelCachePolicy="DontCache" location="Client" /> 
                <add extension=".rar" policy="CacheUntilChange" kernelCachePolicy="DontCache" location="Client" /> 


So it is done :)

Thanks to Aaron Murray

Stress testing with WCAT

Recently I had a case in which I had to prove that load on IIS is too heavy for hosted web applications. While looking for appropriate tool I came across Microsoft Web Capacity Analysis Tool (WCAT) - not very popular tool but very powerful one.

As it wasn't easy to setup and run it I've decided to write an article which summarizes the information you need to run load test with WCAT.

Most of the information is in WCAT 6.3 documentation but I found that it isn't natural easy to run it.

Read Stress testing with Microsoft Web Capacity Analysis Tool (WCAT) article and I hope you'll find it helpful.

Any feedback is appreciated.

WebResource.axd reported Error 404, images - 403.1 - Forbidden

Recently I had interesting experience with web applications that deserves a blog post. We decided to run broken link checker on huge ASP.NET site on staging server (Win 2003 & IIS6). This could also act as a small load test.

So far so good but I've noticed two main reports:

  • WebResource.axd with one specific parameter reported Error 404.0 - File not found
  • some requests to images (.gif) reported Error 403.1 - Forbidden

These errors are logged on IIS log. 

This is quite strange because when I try to run them in the browser manually everything is fine. The hardware seems not very loaded.


While investigating it further (and getting a clue from IIS.NET Forums) I got that IIS is actually trying to executed .gif files (See IIS Status codes). Probably same happens to WebResource.axd...

Then I went on link checker side and noted that it makes "HEAD" request (point 9.4) in order to reduce traffic.


I wrote this little tool to check my assumption.

1: using System;
2: using System.Net;
3: using System.Threading;
5: namespace UrlChecker {
6:     class Program {
7:         static string URL_TO_CHECK2 = "http://localhost/WebResource.axd?d=long-param-here";
8:         static string URL_TO_CHECK = "http://localhost/Photo/1116.gif";
9:         static int LOOPS_NUM = 10;
10:         static int THREADS_NUM = 10;
11:         static int REQUEST_TIMEOUT = 10000;
12:         static string REQUEST_METHOD = "HEAD"; // or "GET"
15:         static ManualResetEvent sync = null;
16:         static void Main(string[] args) {
17:             sync = new ManualResetEvent(false);
18:             Thread[] threads = new Thread[THREADS_NUM];
19:             for (int i = 0; i < THREADS_NUM; i++) {
20:                 threads[i] = new Thread(new ParameterizedThreadStart(CheckLink));
21:                 threads[i].Start(i);
22:             }
23:             sync.Set();
24:             Console.Write("Press 'Enter' to exit!");
25:             Console.ReadLine();
26:         }
28:         static void CheckLink(object ThreadId) {
29:             sync.WaitOne();
31:             for (int i = 0; i < LOOPS_NUM; i++) {
32:                 string statusCode;
33:                 try {
34:                     HttpWebRequest rq = (HttpWebRequest)WebRequest.Create(URL_TO_CHECK);
35:                     rq.Method = REQUEST_METHOD;
36:                     rq.Timeout = REQUEST_TIMEOUT;
37:                     HttpWebResponse resp = (HttpWebResponse)rq.GetResponse();
38:                     statusCode = resp.StatusCode.ToString();
39:                 } catch (WebException ex) {
40:                     statusCode = string.Format("Exception: {0}", ex.Message);
41:                 }
42:                 Console.WriteLine("{0} - '{1}'", ThreadId, statusCode);
43:             }
44:             Console.WriteLine("Thread {0} finished.", ThreadId);
45:         }
46:     }
47: }

And this helped me to confirm it..   Note on line 12 is set HEAD request and all requests fails with the messages shown above (404 and 403.1 accordingly). When GET request is used everything is OK. Luckily the link checker supported GET request on failed although I had some difficulties setting it.

IIS7 Administration and Customization talk

My talk yesterday went well and I think people found it interesting so I've decided to post slides and demo as well as some useful links

From presentation:

  • Slides - PowerPoint 2007 - (2.4 MB)
  • Demo - VS 2008 project (961 KB)

Some resources: