rick.james
-
IIS 7 C++ Module API Sample: Controlling http.sys caching
In my previous post we saw that http.sys caching can be very useful for high traffic (gigabits/sec) sites. In this post we create a sample module that allows configuring which URLs can enter the cache.
-
Fun with http.sys Caching
There are many caches available to web applications. Today I will play with the http.sys URI cache. It is a simple, fast, kernel-mode cache.
How it Works
First IIS must decide if the response can be stored in the http.sys' cache (devs: See HttpSendHttpResponse's pCachePolicy argument). There are about twenty reasons for IIS to decide something should not get in the cache (because of the cache's simple design). To find out why a specific request is not being cached, enable "Failed Request Tracing" and look for the HTTPSYS_CACHEABLE event's reason field.
-
FastCGI: Debugging "The FastCGI process exited unexpectedly"
As you can see there are many places where the FastCGI module expects to have a FastCGI process to communicate with. If the FastCGI process disappears at any of these steps, the "The FastCGI process exited unexpectedly" error occurs. If this happens frequently Rapid Failure Protection may be enabled.
-
FastCGI Rapid Failure Protection
There are a lot of similarities between FastCGI's Rapid Failure Protection feature and w3svc/WAS', read here for more information.
-
IIS 7 C++ Module API Sample: Changing Handlers
IIS 7 C++ Module API Sample: Changing Handlers
Introduction
Today's modules looks at how you can programmatically change handlers. RQ_EXECUTE_REQUEST_HANDLER is by far the most common notification used in the IIS 7 pipline. This makes sense because it is where the response is generated. We know how many different response generation techonologies there are out there. The RQ_EXECUTE_REQUEST_HANDLER notification is only delivered to the modules configured in system.webServer/handlers for the request. This is unlike all other request (RQ_*) notifications, where all modules in system.webServer/modules, that have registered for a notification receive it. The system.webServer/handlers configuration syntax is pretty flexible, but obviously cannot do everything for everyone. This post assumes you've already got the hello world module working, so it jumps straight into API discussion.Pipeline
Here is the first portion of request pipeline. (from the top of httpserv.h) You can see there's an entire notification dedicated to handler selection. You can also see that by this point in the pipeline the request is already authenticated and authorized.#define RQ_BEGIN_REQUEST 0x00000001 #define RQ_AUTHENTICATE_REQUEST 0x00000002 #define RQ_AUTHORIZE_REQUEST 0x00000004 #define RQ_RESOLVE_REQUEST_CACHE 0x00000008 #define RQ_MAP_REQUEST_HANDLER 0x00000010 #define RQ_ACQUIRE_REQUEST_STATE 0x00000020 #define RQ_PRE_EXECUTE_REQUEST_HANDLER 0x00000040 #define RQ_EXECUTE_REQUEST_HANDLER 0x00000080 ...
Deciding When to Change Handlers
The whole reason we're writing this module is because the built-in system.webServer/handlers configuration does not meet our needs. So we need our own logic. In this case we're going to target some special logic that Ruby on Rails seems to like: deliver that which can be delivered and render the rest unto Rails. First we check if we have a IScriptMapInfo already associated with the request. If we do it means that system.webServer/handlers could find a handler. If no handler could be found it means we should deliver it to Rails. If we can find a handler then we need some additional checks: first we use IHttpContext::GetFileInfo to check if this request maps to a file on the file system. Finally, some handlers do not need file system backing, so we compare IScriptMapInfo::GetResourceType to 3. (3 is from the system.webServer/handlers definition in %WinDir%\system32\inetsrv\config\schema\iis_schema.xml)MODULE::OnMapRequestHandler( IHttpContext* pContext, IMapHandlerProvider* pProvider ) { ... IScriptMapInfo* pScriptMap = pContext->GetScriptMap( ); ... if( NULL != pScriptMap ) { if( NULL != pContext->GetFileInfo( ) || 3 == pScriptMap->GetResourceType( ) ) { // // if we have a script map AND // - a file info // - or resourceType=Unspecified (3) // then no need to change handlers // goto Finished; } }
Choosing the New Handler
We now know that we want to deliver the request to Rails. One solution at this point would be to implement IScriptMapInfo and set the IScriptMapInfo::GetModules to be our Rails handler module. The problem with this solution is that there is more than one way to run Rails on IIS, so we don't know which module should handle Rails. So we need to expose some configuration to the web server administrator. At this point we could get into schema extensibility, but there's already a perfectly good configuration section that does almost exactly what we want: system.webServer/handlers! :-). So we tweak the URL to make it look like something system.webServer/handlers would understand (replace the stuff after the last slash with index.rb) and then we ask IIS to do the work for us:// // Get handler for pszScriptName/APPEND_PATH // hr = pContext->MapHandler( pSite->GetSiteId( ), pSite->GetSiteName( ), pszFullAppPath, pRequest->GetHttpMethod( ), &pScriptMap, FALSE ); if( FAILED( hr ) ) { goto Finished; } // // Now make that the handler for this request // pProvider->SetScriptMap( pScriptMap );
Deployment
Since this is a C++ module it needs to be in system.webServer/globalModules. Since this module uses RQ_* notifications is need to be in the application module list (system.webServer/modules). This later requirement means you now have a method for turning this custom handler selection on/off, cause system.webServer/modules is configurable at the application level. (e.g. only add the module in web.configs where you want Rails-style handler mapping). Finally, cause we're using the system.webServer/handlers configuration, we should add an entry for *.rb, mine looks like:<location path="" overrideMode="Allow"> <system.webServer> <handlers accessPolicy="Read, Script"> ... <add name="rails (currently broken)" path="*.rb" verb="GET,HEAD,POST" modules="FastCgiModule" scriptProcessor="c:\ruby\ruby.exe" resourceType="Unspecified" /> ...
Expected Behaviour
- http://localhost/a.rb should get an HTTP 500 with detailed error of "<handler> scriptProcessor could not be found in <fastCGI> application configuration." (I still haven't got Rails working as a FastCGI on IIS 7, I'll blog about it when I do)
- http://localhost/app1/recipe/new should get the same HTTP 500 if our module is running and a regular HTTP 404 if not
- "Failed Request Tracing" log should have something like this:
NOTIFY_MODULE_START ModuleName="RubynatorModule" Notification="MAP_REQUEST_HANDLER" HANDLER_CHANGED OldHandlerName="StaticFile" NewHandlerName="rails (currently broken)" NewHandlerModules="FastCgiModule" NewHandlerScriptProcessor="c:\ruby\ruby.exe" NewHandlerType="" NOTIFY_MODULE_END ModuleName="RubynatorModule" Notification="MAP_REQUEST_HANDLER"
Getting Ruby on Rails Working
This is beyond the scope of this article, checkout this excellent wiki entry. -
IIS 7 C++ Module API Sample: Consuming Trace Events
-
Installing PHP 5.2.4 on Windows Server 2008 RC0 in Pictures
Windows Server 2008 RC0 is now available for beta testing. Here are some screenshots of setting up PHP 5.2.4:
-
IIS 7 Talks: Slides
Will and I had the pleasure of presenting IIS 7 to audiences in 6 countries in the last 2 weeks. Sorry to people who found out about the presentations too late to attend. I've attached a copy of the slides we used. You may also want to see the shared hosting on IIS 7 document.
-
IIS 7 Talks: Zagreb, Athens, Brussels and Belgrade
As previously mentioned, in the next two weeks, Will and I will be doing a bunch of IIS 7 day-long talks. (Brett and Isaac will visit other countries, see here for the full list) Final locations and dates:
-
IIS 7 Talks: Reykjavik and London
Will and I will be presenting one day talks, aimed at IT professionals, about IIS 7. Schedule: