IIS7 was revolutionary in opening the IIS web server platform for public extensibility. Prior to that, few software vendors wrote extensions for IIS, using the native ISAPI Filter and Extension APIs. IIS7 completely changed this, creating a public extensibility model on top of which the web server itself was implemented, and opening it for managed development via the familiar ASP.NET API.
Here are a couple things you can do only with native modules, and why you will probably never have to do them.
In the dark ages, there was ISAPI
Before IIS7, the only way to extend IIS was the C++ ISAPI Filter or Extension API. This was as complicated and error prone as any native server development, and also had limited ability to extend IIS. Most of the IIS features themselves were hardcoded into the monolithic IIS core, and ISAPI made it possible to implement some very specific scenarios to “work around” the built-in request processing. Only a few software vendors dared to build ISAPI components, and the rest of the world was forced to buy them to be able to customize IIS.
IIS 7 brought extensibility into the public domain
IIS7 changed this completely, by creating a public extensibility API and literally building itself on top of it. This way, we made sure that anyone could extend IIS to do everything we could do, because we would both build on the same API. As a result, the webserver became a small request processing pipeline with 40+ optional modules that implemented all the features, such as authentication, compression, caching, static file serving, URL rewriting, and so on.
More importantly, IIS7 delivered the Integrated Pipeline, the engine that enabled ASP.NET developers to extend IIS with the ASP.NET IHttpModule and IHttpHandler APIs. This was huge, because it effectively eliminated the need for native C++ development in order to extend IIS.
To make this happen, we had to fit the existing ASP.NET API over the new IIS7 model, without breaking backward compatibility with ASP.NET, and allowing the ASP.NET APIs to have as much power to extend IIS as possible. Also while meeting the performance requirements of the NT PERF team, that were historically based on much faster native-only IIS benchmarks. It was an insanely complicated technological feat, which I’ll talk about in another post.
So, is there anything I cant do with managed modules?
There are still a couple things that you have to write a native module for.
1. Run in PRE_BEGIN_REQUEST.
This is a special event that occurrs at the very beginning of the request, before IIS has mapped the URL to a physical location, and before it has loaded configuration. A few select native modules use this event for very rapid processing, e.g. the IIS Request Filtering module uses it to rapidly reject danerous requests.
2. Use module priorities.
IIS allows modules to register for request notifications using a priority level, which can range between LAST, LOW, MEDIUM, HIGH, and FIRST. ASP.NET modules are always registered as MEDIUM, meaning that any native module can always elect to run before regardless of their configuration order.
3. Send response asynchronously.
In my opinion, this is one of the most significant limitations. ASP.NET by default buffers the response in memory, and only provides a synchronous HttpResponse.Flush() method for flushing it to the client. If you wanted to efficiently spool large responses to hundreds of concurrent users, you’d have to write a native module that uses asynchronous response writes.
Correction: you can flush ASP.NET response asynchronously with ASP.NET 4.5! Very happy this finally happened, just like the new ability to asynchronously preload request entity. Interestingly enough, both of these features were assigned to me back in 2007, but we just ran out of time to build them (and I regretted that ever since).
4. Intercept/filter request POST data.
While ASP.NET modules can filter the incoming request entity body via the HttpRequest.Filter stream, its not a true filtering mechanism because it requires preloading the entire request into memory/disk first. Secondly, that data will be loaded by ASP.NET, and cannot be made available to a non-ASP.NET application (like PHP or classic ASP). You would have to write a native module to efficiently filter the request data on the fly, preload the request entity asynchronously, and make it available to non-ASP.NET clients.
5. Handle global notifications: MapPath, ConfigurationChange, HealthCheck, RSCAQuery, etc
IIS has global notifications that allow native modules to override functionality for mapping URLs to files, handling async configuration changes, responding to external status requests via the Runtime Status and Control API, and more. These are not available to ASP.NET modules.
6. Get precise control over performance and resource use.
When people ask me whether they should write a native module, my answer will always be – never! It just takes too long to build, stabilize, and support native code – and its next to impossible to find developers these days who can maintain it well.
Now, dont get me wrong. There are few things I enjoy more than writing C++ code, and carefully moving bits with precise control over memory, IO, and the win32 API. I’ve personally written quite a few of these very complex, very performance centric native modules, including the IIS7 Bit Rate Throttling module, and LeanServer ScaleUP, the absolutely fastest way to do HTTP uploads to an IIS server. But, as much as I enjoy this, I would never consider writing a native module if its possible to write a managed one, for the same reason that its been years since I’ve written anything in C++.
Nowadays, you can do almost anything with .NET, using the comprehensive support of the .NET framework and plugging any holes with p/invoke to win32 APIs or COM interop. You can even run .NET on Server Core (which you couldnt do for all of 5 minutes when Windows Server 2008 first came out).
Consider this short but sweet list of reasons why you should only build ASP.NET modules:
- They take 10 times less time build, and cost 100 times less to support.
- You can find sample code on how to do anything you ever needed.
- Any .NET developer can write ASP.NET modules, but I wouldnt let most developers near C++ server code.
- ASP.NET modules automatically deploy with your application.
- ASP.NET modules can run in shared hosting environments, including Microsoft Azure Websites, where native modules don’t (they require Administrative priviledges to install).
This is exactly why the ASP.NET Integrated Pipeline was such a huge win for the Microsoft web platform. It removed native development as a requirement for extending the web server, and made it an option for those few that really need it.
So, next time you find yourself tearing your hair out because of an old unsupported ISAPI filter in your application, consider sitting down for a couple hours and rewriting it to an ASP.NET module. It will be faster, cheaper, and you wont have to pay anyone or make international phone calls next time it breaks.