This post is the first of a two part series about handlers and modules and is an attempt to offload what I know about handlers to you. Knowing what handlers are and how they work is what separates the savvy from the not-so-savvy when it comes to IIS administration. Although very few modules in the DotNetNuke ecosystem use/rely on handlers, those that do experience a steady stream of support calls along the lines of “hey, XYZ doesn’t work” and it is down to a handler issue. Examples of modules that use handlers are Ultra Video Gallery and Document Exchange. DotNetNuke uses handlers itself as well. So what is a handler? In a nutshell a handler is piece of code that handles a web request. To understand why it’s good to know more about this you need to look closer at http traffic (i.e. the bits and bytes that go back and forth between your browser and the server).
Before we go any further I advise you to get Fiddler (http://www.fiddler2.com). Fiddler is an amazingly simple yet powerful tool for examining http traffic. What the program does is to temporarily create a proxy on your local PC. I.e. is nestles itself between your Internet Explorer (it works like that with IE and Chrome, for Firefox you need a plugin) and your network card. This way everything all traffic (well, only http traffic actually) between your browser and the internet goes through the program. It can then show you this traffic. This is a screenshot of its main UI:
I use Fiddler on an almost daily basis. It is the essential tool to have to debug handler issues. I also wrote a blog post on how to debug DMX WebDAV traffic specifically using Fiddler. The UI is split vertically in half. On the left you find the so-called sessions. A session is a pair of request and responses. On the right we can drill down into any one such session. My preferred way to examine traffic is using the “raw” view (shown above). So on the right I select the “Inspectors” tab which shows me details of the request and response and I select “raw” on both the top and bottom panel to show me exactly what went over the wire. I see both the headers and the content of the traffic.
Requests and responses
Whenever you type a url in your browser, it emits a request over the internet. You need to specify a server (or host as it’s called) and a resource. So the request
requests resource “/default.aspx” from host “www.bring2mind.net”. The host is looked up through the DNS and resolved to an IP address so the browser knows who to call. Then the server is contacted and the resource is requested. At this point I’m going to assume the server is running IIS. I’m sure Apache will do things similarly but here we’re just concerned with IIS. So the browser has contacted the server and requested the resource. It does so through a specific port (this is defaulted in http to port number 80) and with a particular verb. The verb specifies what the browser wants with the resource. In regular http traffic you’ll only see two verbs: GET and POST. GET is the most common and tells the server “gimme this resource”. POST is used when you click a button to submit a form and we won’t go any further into details about that. The most important thing to note at this point is that a full web request consists of a host, a port, a resource and a verb. In Fiddler you can see these details as you request a web page. At the top of the request panel you’ll see the “GET http … etc” ending with HTTP/1.1 which is telling the server the browser understands http protocol 1.1.
Any request can be accompanied by a number of headers. Headers are pairs of “Attribute: Value” pairs. So a common header like “User-Agent: Mozilla/5.0 (Windows NT 6.1 … etc” tells the server we are arriving with a Mozilla browser and running Windows 7. This header informs the server what browser and O/S we are using. It can also be used to determine if this is a search engine (e.g. Google) that is crawling our site. A very important header to note at this point is the Cookie header. The cookie header sends, as you’ve probably guessed, the cookies to the server.
The response from the server begins with a line stating the status of the response. Normally this will be “200 OK”. This means the request can be answered and the browser will get what it asked for. There are a whole bunch of status codes in http and if you’re interested you can read more about those here. Fiddler will color the sessions in the list on the left in part based on these response codes: the red ones are 4xx responses meaning there was an error. Like the request the response can also have any number of headers. These headers will be an important indication of what is running on the server and as we’ll see later, who is “picking up the call”.
Who is answering the phone?
Whenever the browser makes a request to a server, the server has to determine what needs to happen. In the “classic” web world, the web server just served out files that were on its hard disk. These files typically had extensions like .html, .js etc. A web server was no more than a file server. But this all changed when “web applications” came about. Here the response from the server was not some fixed file on the server but the result of some program running on the server. ASP.NET is but one of these mechanisms and IIS supports many of these. So when you request “/Default.aspx” the server must be told this is not just a file on the hard disk but that ASP.NET knows how to handle the request. This is where the word handler comes from.
So how does IIS determine what should happen? Well, it first assumes that the resource is actually there as a file and it should just serve it. It then looks at its own configuration if there is no exception to this. In IIS 6 this is slightly different than in IIS 7. In IIS 6 it is a two stage process where IIS first looks at the “Application Configuration”:
Here you can see aspx is mentioned. If you open this up you’ll find it points to c:\windows\microsoft.net\framework\v2.0.50727\aspnet_isapi.dll. This is the base ASP.NET component responsible for handling ASP.NET requests. If you open up the setting you’ll see this:
Here you see something else that is interesting: the handlers can be limited by verb. The above is telling the server that only for GET, HEAD, POST, and DEBUG verbs should IIS redirect the request to ASP.NET. You’ll find that using another verb the server will respond with an error as it tries to serve up the raw aspx file which is prevented internally from happening (security). Which brings me to something very important to note here:
If there is no handler for the requested resource it is assumed that it is a file on disk and IIS will look for it and attempt to serve it.
For instance, if you request a jpg file (which by default is not mentioned in the handlers) it will just find it on disk and serve it.
IIS 6 vs IIS 7
After IIS 6 has gone through its application configuration and determined the request is for asp.net the so-called ISAPI handler for ASP.NET takes over. This handler then goes to the root of the web application’s directory and looks for the web.config file. The main difference with IIS 7 is that in the latter there is no “Application Configuration” any more. Instead ALL handlers are in config files (which incidentally also makes IIS 7 settings more portable than IIS 6 settings). For both IIS versions there is something called a machine.config which determines the configuration of the “default” website and then a web.config which outline the specific configuration of a particular web site. So the above is an intermediate stage in determining handlers only used in IIS 6.
The significance of machine.config and web.config
So ASP.NET is going to handle the request. Now you may say this is the end of the line. ASP.NET will just “run the script” and we’re done. But no. ASP.NET can be tuned to send requests to different bits of code. How is this useful? Well, lets take the example of a thumbnail generator. You’ve created some code which takes an image (e.g. a jpg stored on your server’s hard disk) and resize this to a thumbnail and serve this to a browser. This is perfectly possible in ASP.NET. You could do this as an .aspx page but this is not really the “correct” way to do this. An .aspx page is expected to emit an HTML page after all. So what you do is to create a handler. And you tell the ASP.NET handler that if the request is of the kind “/thumbnail.axd?file=mypic.jpg”, then you would like handle the request because you know what to do. This is done in the web.config file which is an XML file. The web.config has section where it lists all the applications handlers which tell ASP.NET how to route the call.
More IIS 6 vs 7
To keep us on our toes, Microsoft has changed the way this is done from IIS 6 to 7. The IIS 6 handler section is under configuration/system.web/httpHandlers and the IIS 7 handler section is under configuration/system.webServer/handlers. This has not been too great for those of us working to support customers as we first need to determine which IIS version the customer is using before we can continue. Note that IIS 6 will happily ignore the IIS 7 handlers section and visa versa. For the remainder of this post we will assume we are working on IIS 7.
This is how the IIS 7 handler section looks for a default DNN 6 installation:
If you examine the first one you’ll see that the request for “Logoff.aspx” is redirected to type DotNetNuke.Services.Authentication.LogOffHandler, DotNetNuke. I.e. to class DotNetNuke.Services.Authentication.LogOffHandler in DotNetNuke.dll. If you open up the source version of DNN you can find this class and you’ll find it implements an HttpHandler! And also you’ll find there is no actual file Logoff.aspx in the distribution. Instead if anywhere you use Logoff.aspx you will be logged off.
The web.config is specific to a web site and is always found in the root of the application. But this is only part of the story. There is also a machine.config (which looks the same but is a whole lot longer) which is hidden deep in your system files. This is the “base” configuration. Most importantly it tells IIS the default behavior for a site and what a web.config is allowed to change in this. Normally we don’t need to touch this file. But you should be aware of its existence and significance. The web.config is meant to be editable by the site administrator. The machine.config is locked down and only accessible by the server’s administrator.
IIS 7 handler administration
If you open up IIS Manager in IIS 7 you’ll see that an application has the following management screen:
Under IIS you’ll see “Handler Mappings”. Click to open and you’ll see something like this:
What you’re looking at is a merged set of handlers from both the machine.config (i.e. the default) and the web.config! So even though the .aspx handler is not mentioned in the web.config you’ll see it here on the list. All changes made to this list go into the web.config. So you can remove a handler which shows up in the web.config as a “remove” node. This is a major advantage of IIS 7 over IIS 6 where this did not exist.
The can of worms
So let me give an example of how being unaware of handlers can get you into trouble. In one of my modules I naively thought I’d add a custom rss feed aspx. So I went ahead and added Rss.aspx to my module and began programming my logic. But whatever I did, whenever I requested /DesktopModules/Bring2mind/MyModule/Rss.aspx, I’d get the core rss feed. Now look at the handlers section of DNN in the picture shown earlier again. Aha. RSS.aspx is wired to DotNetNuke.Services.Syndication.RssHandler, DotNetNuke. That means that my code would never run as ASP.NET had redirect the request (and subsequently processing) to this handler. All I needed to do to resolve this issue is to rename the aspx and I was fine again. I just needed to avoid getting my call trapped.
But this illustrates a complexity particular to DotNetNuke extension programming. If you were to make your own web application, you’d be well aware and in full control of the handlers. But in DNN module development we don’t control the web.config. Sure, there are now mechanisms to make changes to the web.config when installing a module, but this still leaves us open to mishaps when DotNetNuke or some other module makes changes to the web.config. Not too long ago, when DotNetNuke added the Telerik components to the framework, a handler was added for the upload control for Telerik. Unfortunately for me, DMX used a similar handler for its own (also based on Telerik) upload component. The result was that the DMX upload broke on the DNN upgrade. There are no guilty parties in this. It is part of the game when you’re in this field.
Debugging handlers – some pointers
So something that uses a handler broke. Now what? My entry point is Fiddler and the web.config. I fire Fiddler up and do whatever it takes to reproduce the error in the browser and begin to examine the http traffic (tip: compare the traffic between two sites if you have one where things are working correctly and one that has an error). It’s often not hard to find the calls you’re looking for. In the case of an upload component complaining it can’t find the handler then check those calls and see what the server response is. Often we don’t see the error returned by IIS on screen as the calls are Ajax calls which remain hidden from view. But with Fiddler we can examine the response before it got to the browser and find more detail. Do you see an ASP.NET error? If so you’ll probably see a name of a component that produced the error. Is that component the component you expected to handle the request, or another. If it is an unexpected component then there is an issue in the routing most likely. Get the web.config and read through the (correct) handler section and see where it could have veered off track. Note the handlers have an order to them! I.e. the first handler that is a hit gets the call and subsequent ones are ignored. If the request was not handled by our designated component but we are stuck why this is so we can go to the next stage.
The second stage is more intrusive and is for IIS 7. In the advanced settings of your web application you can set the “Failed Request Tracing”:
Then you need to go to the “Failed Request Tracing Rules”
There you can add a rule that specifies which failures interest you. The analysis files are stored in the location specified in the Advanced Settings panel shown above. It will allow you to see how ASP.NET has walked through the various bits and determined which handler to use.
At this point there are no more general pointers to give. Instead I suggest that you Google your way through what you find. The specifics of the application come into play here and things can get very complex. In a next segment I’ll look at (ASP.NET) modules. They go together with handlers like peas and carrots.
NB: Cross-posted from Blog2mind