Developing IIS Native module
I have started to learn IIS Native API (C++) and hope to share what I have learned. This blog is targeting for beginner programmers who want to develop IIS native module in C++ for the first time.
What I will cover here
- Understanding Global Module/Module/Handler
- Quick reference of IIS Native API
- IIS Core system
Understanding Global Module/Module/Handler
In order to learn IIS native API, I tried to create a small module with various sample codes and found some cases of not working. In the explanation of the popular example module, I had learned that I needed to register the module onto both <globalModules> and <modules> section. And it worked with no question.
- TIP
- My first module did not work because I complied the module as x86 version and wanted to make it run on amd64 bit machine. The problem was solved with compiling the module as 64 bit compilation)
- Another mistake happened when I used the full path of my module dll file which is created by Visual Studio without access permission. I had to give the access permission for the users group for the directory to solve the error such as "The Module DLL c:workIISNativeModuleDebugMyModule.dll failed to load. The data is the error." which was shown in the event viewer.
My first module was to subscribe a single global level event such as GL_PRE_BEGIN_REQUEST and it worked in that way. And soon I realized the module works even though I removed the line of module from the <modules> section and I realized that if the module needs to subscribe only global level events, we don't need to register module onto <modules> section. This is how I learned the difference between module and global module in configuration wide.
I also tried to create another module which subscribes a single request level event such as RQ_EXECUTE_REQUEST_HANDLER. But this time, the module did not work even though I registered in the both <globalModules> and <modules> section.
I realized that actually I had made a handler module even though I thought I was making an ordinary module and I had to configure addional thing. Handler module is a special module which subscribes only the RQ_EXECUTE_REQUEST_HANDLER event. So, so the problem had to be solved by registering the module to <handlers> section.
But, I ran into another problem after I put my module, "TestModule", at the end of the value of modules attribute like this.
<add name="StaticFile" path="*" verb="*" modules="StaticFileModule,DefaultDocumentModule,DirectoryListingModule, TestModule" resourceType="Either" requireAccess="Read" />
When I sent a request but the module was not invoked. And the problem was solved by changing the order of the module names like this.
<add name="StaticFile" path="*" verb="*" modules=" TestModule ,StaticFileModule,DefaultDocumentModule,DirectoryListingModule" resourceType="Either" requireAccess="Read" />
That means, if there are multiple modules in a single handler item such as the above case, the previous modules can determine to make the next module run or not. And that's why my module was not picked up.
If you want to know more about this topic, refer to http://blogs.msdn.com/b/tmarq/archive/2007/08/30/iis-7-0-asp-net-pipelines-modules-handlers-and-preconditions.aspx.
Quick reference of IIS Native API
While I read MSDN, the number of classes/interfaces/struct/constants made me confused and overwhelmed. So, to get rid of the complexity, I tried to summarize the relationship between each interfaces and event handlers. After that, I felt it was not that complicated actually. Here is my version of summurized set of API, which I attached here for you to find the overal view of whole API sets.
1) Classes which are implemented in global or regular module (or handler module)
- CGlobalModule, which is for global module
- IHttpModuleFactory, which is a class factory of regular module
- CHttpModule, which is for regular module
2) The order of creating the class objects
- All objects should be created when RegisterModule() function, which is the unique exported DLL function of the module
- CGlobalModule object is created at the heap memory and then its pointer is passed to IIS worker process by calling SetRequestNotifications() function
- IHttpModuleFactory object is also created at the heap memory and then its pointer is passed to IIS worker process by calling SetGlobalNotifications() function
- HttpModule object is created when IHttpModuleFactory object's GetHttpModule() function is called by IIS worker process
- NOTE:
- a. When GetHttpModule() is called, which happens for once for every request, we should create the HttpModule object every time and the object should be deleted if it was created in the heap memory when each request life cycle is finished
- b. If you want to create a the module in heap, not stack memory, you can use IModuleAllocator interface to allocate memory of request pool
- c. CGlobalModule event handler functions
3) Handler function name ant its event provider type
- OnGlobalApplicationPreload
- IGlobalApplicationPreloadProvider
- OnGlobalApplicationResolveModules
- IHttpApplicationResolveModulesProvider
- OnGlobalApplicationStart
- IHttpApplicationStartProvider
- OnGlobalApplicationStop
- IHttpApplicationStopProvider
- OnGlobalCacheCleanup
- void
- OnGlobalCacheOperation
- ICacheProvider
- OnGlobalConfigurationChange
- IGlobalConfigurationChangeProvider
- OnGlobalCustomNotification
- ICustomNotificationProvider
- OnGlobalFileChange
- IGlobalFileChangeProvider
- OnGlobalHealthCheck
- void
- OnGlobalPreBeginRequest
- IPreBeginRequestProvider
- OnGlobalRSCAQuery
- IGlobalRSCAQueryProvider
- OnGlobalStopListening
- IGlobalStopListeningProvider
- OnGlobalThreadCleanup
- IGlobalThreadCleanupProvider
- OnGlobalTraceEvent
- IGlobalTraceEventProvider
- OnGlobalTerminate
- void
- Return value which are applied to all event handlers
- GL_NOTIFICATION_CONTINUE
- GL_NOTIFICATION_HANDLED, which indicates to stop IIS pipeline
4) CHttpModule event handler functions
- Handler function name ant its event provider type
- OnAcquireRequestState
- OnPostAcquireRequestState
- OnAsyncCompletion
- OnAuthenticateRequest (IAuthenticationProvider)
- OnPostAuthenticateRequest
- OnAuthorizeRequest
- OnPostAuthorizeRequest
- OnBeginRequest
- OnPostBeginRequest
- OnPostEndRequest
- OnCustomRequestNotification (ICustomNotificationProvider)
- OnEndRequest
- OnExecuteRequestHandler
- OnPostExecuteRequestHandler
- OnLogRequest
- OnPostLogRequest
- OnMapPath (IMapPathProvider)
- OnMapRequestHandler
- OnPostMapRequest
- OnPreExecuteRequestHandler
- OnPostPreExecuteRequestHandler
- OnReleaseRequestState
- OnPostReleaseRequestState
- OnResolveReuqestCache
- OnPostResolveRequestCache
- OnUpdateRequestCache
- OnPostUpdateRequestCache
- OnReadEntity (IReadEntityProvider)
- OnSendResponse (ISendResponseProvider)
- Return value which are applied to all event handlers
- RQ_NOTIFICATION_CONTINUE
- RQ_NOTIFICATION_FINISH, which indicates to stop IIS pipeline from request level
- RQ_NOTIFICATION_PENDING, which indicates to stop IIS pipeline temporarily while module's executing its own task asynchronously. There are two ways to do this such as using IHttpContext::ExecuteRequest and OnAyncCompletion() and IHttpContext::PostCompletion()
5) IHttpEventProvider and derived interfaces
- IHttpEventProvider
- IAuthenticationProvider
- ICacheProvider
- IHttpCacheKey <==GetCacheKey()
- IFileKey
- IHttpTokenKey
- IUriKey
- IHttpCacheSpecificData <==GetCacheRecord()
- IHttpTokenEntry
- ICustomNotificationProvider
- IGlobalApplicationPreloadProvider
- IGlobalApplicationPreloadProvider2
- IGlobalConfigurationChangeProvider
- IGlobalFileChangeProvider
- IHttpFileMonitor <==GetFileMonitor()
- IGlobalRscaQueryProvider
- IGlobalStopListeningProvider
- IGlobalThreadCleanupProvider
- IGlobalTraceEventProvider
- IHttpApplicationProvider
- IHttpApplicationResolveModulesProvider
- IPreBeginRequestProvider
- ISendResponseProvider
- IMapHandlerProvider
- IMapPathProvider
- IReadEntityProvider
- IHttpApplicationStartProvider
6) Relationship between Interfaces
- IHttpCompletionInfo <==CHttpModule::OnAsyncCompletion()
- IHttpCompletionInfo2
- IHttpContext
- IHttpApplication <==GetApplication()
- IHttpApplication2
- IHttpConnection <==GetConnection()
- IHttpFileInfo <==GetFileInfo()
- IMetadataInfo <=GetMetadata()
- IReferencedMetadataInfo
- IHttpModuleContextContainer <==GetModuleContextContainer()
- IHttpStoredContext <==GetModuleContext()
- IWebSocketContext
- IHttpConnectionStoredContext
- IDispensedHttpModuleContextContainer
- IHttpSite <==GetSite()
- IHttpUser <==GetUser()
- IHttpTraceContext <==GetTraceContext()
- IHttpUrlInfo <==GetUrlInfo()
- IHttpResponse <==GetRespons()
- IHttpCachePolicy <==GetCachePolicy()
- IHttpCachePolicy2
- IHttpResponse2
- IHttpRequest <==GetRequest()
- IHttpRequest2
- IHttpRequest3
- IScriptMapInfo <==GetScriptMap()
- IHttpContext2
- IHttpContext3
- INamedContextContainer <==GetNamedContextContainer()
- IHttpModuleRegistrationInfo
- IHttpModuleFactory==> SetRequestNotifications()
- IModuleAllocator <==GetHttpModule()
- IHttpServer <==RegisterModule
- IHttpServer2
- IHttpPerfCounterInfo <==GetPerfCounterInfo() (IHttpSite::GetPerfCounterInfo() has the function)
IIS Core system
While summarizing the whole IIS native API, there are another confusing thing regarding the return value of the event handler.
At first, I imagined there should be only two such as "Continue" and "Finish", one for succes and the other for failure. But I realized that there was one more return value such as "RQ_NOTIFICATION_PENDING".
MSDN explains that it is used for asynchronous task handling but it was not that helpful because I didn't know how IIS and module should work together.
So before understanding that, I had to make some curiosity about how IIS actually works and finally I managed to make some big ficture of IIS internal system and even made a very simple program which explains the relationship between the important Interface classes and our module classes which we should develop. The below attached code is compliable code in any C++ compiler. I just wanted to create my own simplified version of important IIS native API interfaces and classes. The purpose of the small program was to simulate how IIS handles the request and call IIS modules in its module pipeline, supposing there is only one module. Actual life cycle of request would be like as the following steps though:
- IIS reads the applicationhost.config and prepare a list data structure of module objects in order to make all of the modules work for the request; Each module object implement event handler functions.
- Now, IIS is ready and OS kernel system such as HTTP receive a request from wire and passes the request object to IIS
- And then IIS creates a HttpContext object and copy the pointer of the request object to the HttpContext object, which will be used by modules
- IIS creates a IISEventHandler object and the pointer of the HttpContext object is also copied to it
- IIS call the event handler function of each module with the parameter of the IISEventHandler object
- Each module participate in manipulating the request object and response body in its event handler functions
- Once all modules finish the event handler functions, IIS give the response body to OS kernel system such as HTTP service
Summary
Today, I tried to review what I learned while trying the sample example codes of http://msdn.microsoft.com/en-us/library/aa347664(v=vs.90).aspx (IIS Native-Code Extensibility API Reference).
In the next time, I will write my own module to experiment more and share about that.
Source code of IIS and module simulation
#include "stdafx.h" #include <iostream> using namespace std; class IHttpContext { public: virtual void SetHttpRequest(int newValue)=0; }; class IHttpEventProvider { public: virtual IHttpContext* GetHttpContext()=0; }; class HttpContext : IHttpEventProvider, IHttpContext { private: int* _pHttpRequest; public: HttpContext(int* pHttpRequest) : _pHttpRequest(pHttpRequest) { } void SetHttpRequest(int newValue) { *_pHttpRequest=newValue; } IHttpContext* GetHttpContext() { return this; } }; class HttpEventProvider: IHttpEventProvider { private: IHttpContext* _pHttpContext; public: HttpEventProvider(IHttpContext* pHttpContext) : _pHttpContext(pHttpContext) { } virtual IHttpContext* GetHttpContext() { return _pHttpContext; } }; class IModule { public: virtual void OnBeginRequest(IHttpEventProvider* provider)=0; }; class Module : IModule { public: void OnBeginRequest(IHttpEventProvider* pHttpEventProvider) { IHttpContext* pHttpContext=pHttpEventProvider->GetHttpContext(); pHttpContext->SetHttpRequest(3); } }; int _tmain(int argc, _TCHAR* argv[]) { // Step 1. // let's suppose httpRequest object is integer value and was created in the beginning. // In actual world, the request object is created by http sys service for every request int httpRequest=0; cout << "Original Request: " << httpRequest << endl; // Step 2. // Now, IIS create the module object here to process the request. //In the actual world, it was done by IIS's calling the IHttpModuleFactory::GetModule() method in each module .dll file. Module* pModule=new Module(); // Step3. // After creating module object, IIS create HttpContext object with copying the httpRequest object inside it. HttpContext* pHttpContext=new HttpContext(&httpRequest); // Step4 // And then, IIS creates httpEventProvider object, which will be used by the parameter object of the event handler function which is implemented in each module .dll file // As you can see here, the HttpContext object information copied to httpEventProvider object so that event handler can access the httpContext object HttpEventProvider httpEventProvider((IHttpContext*) pHttpContext); // Step5 // IIS creates state machine which invokes each deterministic request level events handler which is implemented in each module .dll file. // In actual world, there are global level event and non-deterministic request level events and they are fired outside of this state machine. IModule* pIModule; pIModule=(IModule*) pModule; pIModule->OnBeginRequest((IHttpEventProvider *) pHttpContext); // Step6 // Now the state machine is finished and then response object is passed to the http sys service and it transfer it to the client which started the httpRequest. cout << "Modified request by module: " << httpRequest << endl; // Step7 // After all of the request is finished, the httpContext and module object is deleted to clean up delete pModule; delete pHttpContext; // In actual world, the event handler function can be implemented to support to handle asyncronous tasks but it wasn't explained here. I will explain it later. return 0; }