Thursday, June 24, 2010

HTTP Modules and HTTP Handlers


In the Internet world, Web servers serve resources that have been put on the Internet and provide other services like security, logging, etc.

At the beginning of the Internet era, clients' needs were very limited; .htm files were often satisfactory. As time passed, however, clients' requirements extended beyond the functionality contained in .htm files or static files.

Developers needed a way to extend or complement the functionality of Web servers. Web server vendors devised different solutions, but they all followed a common theme: "Plug some component into the Web server". All Web server complement technologies allowed developers to create and plug in components for enhancing Web server functionality. Microsoft came up with ISAPI (Internet Server API); Netscape came up with NSAPI (Netscape Server API), etc.
ISAPI is an important technology that allows us to enhance the capabilities of an ISAPI-compliant Web server (IIS is an ISAPI-compliant Web server). The following components serve this purpose:
  • ISAPI Extensions
  • ISAPI Filters
ISAPI extensions are implemented by using Win32 DLLs. You can think of an ISAPI extension as a normal application. ISAPI extensions are the target of http requests. That means you must call them to activate them.


Think of an ISAPI filter as just that: a filter. It sits between your Web server and the client. Every time a client makes a request to the server, it passes through the filter.

Clients do not specifically target the filter in their requests, rather, clients simply send requests to the Web server, and then the Web server passes that request to the interested filters.

Filters can then modify the request, perform some logging operations, etc.

It was very difficult to implement these components because of the complexities involved. One had to use C/C++ to develop these components, and for many, development in C/C++ is a pain.

So what does ASP.NET offer to harness this functionality? ASP.NET offers HttpHandlers and HttpModules.

ASP.NET Request Processing

ASP.NET request processing is based on a pipeline model in which ASP.NET passes http requests to all the modules in the pipeline. Each module receives the http request and has full control over it. The module can play with the request in any way it sees fit. Once the request passes through all of the HTTP modules, it is eventually served by an HTTP handler. The HTTP handler performs some processing on it, and the result again passes through the HTTP modules in the pipeline.

The following figure describes this flow. 




Notice that during the processing of an http request, only one HTTP handler will be called, whereas more than one HTTP modules can be called.

Http Handlers

HTTP handlers are the .NET components that implement the System.Web.IHttpHandler interface. Any class that implements the IHttpHandler interface can act as a target for the incoming HTTP requests. HTTP handlers are somewhat similar to ISAPI extensions. One difference between HTTP handlers and ISAPI extensions is that HTTP handlers can be called directly by using their file name in the URL, similar to ISAPI extensions.

HTTP handlers implement the following methods.

                       
  

Method Name
  

Description
  

ProcessRequest
  

This method is actually the heart   of all http handlers. This method is called to process http requests.
  

IsReusable
  

This property is called to   determine whether this instance of http handler can be reused for fulfilling   another requests of the same type. HTTP handlers can return either true or   false in order to specify whether they can be reused.


These classes can be mapped to http requests by using the web.config or machine.config file. Once that is done, ASP.NET will instantiate http handler whenever the corresponding request comes in. We will see how to specify all of these details in web.config and/or machine.config files.

ASP.NET also supports the creation of http handlers by means of the IHttpHandlerFactory interface. ASP.NET provides the capability of routing http requests to an object of the class that implements the IHttpHandlerFactory interface. Here, ASP.NET utilizes the Factory design pattern. This pattern provides an interface for creating families of related objects without specifying their concrete classes. In simple terms, you can consider such a class as a factory that creates http handler objects depending on the parameters passed to it. We don't have to specify a particular http handler class to instantiate; http handler factory takes care of it. The benefit of this is if in the future the implementation of the object that implements the IHttpHandler interface changes, the consuming client is not affected as long as the interface remains the same.

These are following methods in IHttpHandlerFactory interface:

                       
  

Method Name
  

Description
  

GetHandler
  

This method is responsible for   creating the appropriate handler and returns the reference out to the calling   code (the ASP.NET runtime). Handler object returned by this method should   implement the IHttpHandler interface.
  

ReleaseHandler
  

This method is responsible for   releasing the http handler once request processing is complete. The   implementation of the factory decides what it should do. Factory   implementation can either actually destroy the instance or return it to a   pool for future requests.


Registering HTTP Handlers and HTTP Handler Factories in Configuration Files

ASP.NET maintains its configuration information in the following configuration files:
  • machine.config
  • web.config

machine.config file contains the configuration settings that apply to all the Web applications installed on that box.web.config file is specific to each Web application. Each Web application can have its own web.config file. Any sub directory of a Web application may have its own web.config file; this allows them to override the settings imposed by the parent directories.

We can use <httpHandlers> and <add> nodes for adding HTTP handlers to our Web applications. In fact the handlers are listed with <add> nodes in between <httpHandlers> and </httpHandlers> nodes. Here is a generic example of adding an HTTP handler:



<httpHandlers>
               <add verb="supported http verbs" path="path" type="namespace.classname, assemblyname" />
<httpHandlers>


In the above XML,
  • The verb attribute specifies the HTTP verbs supported      by the handler. If the handler supports all of the HTTP verbs, simply use      "*", otherwise list the supported verbs in a comma separated      list. So if your handler supports only HTTP GET and POST, then verb      attribute will be "GET, POST".
  • The path attribute specifies the path or wildcard      specification of the files for which this handler will be invoked. For      example, if you want your handler to be called only when test.xyz file is      requested, then the path attribute will contain "test.xyz";      similarly if you want your handler called for any file having .xyz      extension, the path attribute will contain "*.xyz".
  • The type attribute specifies the actual type of      the handler or handler factory in the form of a combination of namespace,      class name and assembly name. ASP.NET runtime first searches the assembly      DLL in the application's bin directory and then searches in the Global      Assembly Cache (GAC).
Use of HTTP Handlers by the ASP.NET Runtime

Believe it or not, ASP.NET uses HTTP handlers for implementing a lot of its own functionality. ASP.NET uses handlers for processing .aspx, .asmx, .soap and other ASP.NET files.

The following is the snippet from the machine.config file:

<httpHandlers>
<add verb="*" path="trace.axd" type="System.Web.Handlers.TraceHandler"/>
 <add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory"/>
 <add verb="*" path="*.ashx" type="System.Web.UI.SimpleHandlerFactory"/>
 <add verb="*" path="*.config" type="System.Web.HttpForbiddenHandler"/>
<add verb="GET,HEAD" path="*" type="System.Web.StaticFileHandler"/>
</httpHandlers>



You can see in the above configuration that all the requests for .aspx files are processed by the System.Web.UI.PageHandlerFactory class. Similarly all the requests for .config and other files, which should not be directly accessible to the clients, are handled by the System.Web.HttpForbiddenHandler class. As you might have already guessed, this class simply returns an error to the client stating that these kinds of files are not served.

Implementing HTTP Handlers

Now we will see how to implement an HTTP handler. So what should our new handler do? Well, as I stated above, handlers are mostly used for adding new functionalities to Web servers; therefore, we will create a handler that handles new types of files, files that have a .Myhandler extension. Once we implement this handler and register it in the web.config file of our Web application, all will be handled by this new handler.

Steps involved in implementing HTTP handler are as follows:
  1. Write a class which implements IHttpHandler interface
  2. Register this handler in web.config or machine.config      file.
  3. Map the file extension to ASP.NET ISAPI extension DLL      (aspnet_isapi.dll) in Internet Services Manager.

Step1
Create a new C# Class Library project in Visual Studio.NET and name it "MyHandler". Visual Studio.NET will automatically add a class named "Class1.cs" into the project. Rename it "NewHandler"; open this class in the code window and change the class name and constructor name to "NewHandler".

The following is the code for the NewHandler class.

using System;
using System.Web;
namespace MyHandler
{

    public class NewHandler : IHttpHandler
    {
        public NewHandler()
        {
        }
        #region Implementation of IHttpHandler

        public void ProcessRequest(System.Web.HttpContext context)
        {
            HttpResponse objResponse = context.Response;
            objResponse.Write("<html><body><h1>Hello World ");
            objResponse.Write("</body></html>");
        }
        public bool IsReusable
        {
            get
            {
                 return true;
            }
        }
        #endregion
    }
}

As you can see in the ProcessRequest method, the HTTP handler has access to all ASP.NET intrinsic objects passed to it in its parameter through the System.Web.HttpContext object. Implementing the ProcessRequest method is simply extracting the HttpResponse object from the context object and then sending some HTML out to the client. Similarly, IsReusable returns true to designate that this handler can be reused for processing the other HTTP requests.

Let's compile it and place it in the bin directory of the webapp virtual directory.

Step 2

Register this handler by adding the following text in the web.config file:

<httpHandlers>
  <add verb="*" path="*.Myhandler " type="MyHandler.NewHandler,MyHandler"/>
</httpHandlers>

Step 3

Since we are creating a handler for handling files of a new extension, we also need to tell IIS about this extension and map it to ASP.NET. If we don't perform this step and try to access the Hello.Myhandler file, IIS will simply return the file rather than pass it to ASP.NET runtime. As a consequence, the HTTP handler will not be called.


Launch the Internet Services Manager tool, right click on Default Web Site, select Properties, go to Home Directory tab and press Configuration button. This will popup Application Configuration dialog. Click Add button and fill the Executable field with the path to the aspnet_isapi.dll file and fill . Myhandler in the Extension field.

Session State in HTTP Handlers

Maintaining session state is one of the most common tasks that Web applications perform. HTTP handlers also need to have access to the session state. But session state is not enabled by default for HTTP handlers. In order to read and/or write session data, HTTP handlers are required to implement one of the following interfaces:
  • IRequiresSessionState
  • IReadOnlySessionState.
An HTTP handler should implement the IRequiresSessionState interface when it requires read-write access to the session data. If a handler only needs read access to session data, then it should only implement the IReadOnlySessionState interface.

Both of these interfaces are just marker interfaces and do not contain any methods. So if we want to enable session state for our NewHandler handler, then declare the NewHandler class as followed:

public class NewHandler : IHttpHandler, IRequiresSessionState

HTTP Modules

HTTP modules are .NET components that implement the System.Web.IHttpModule interface. These components plug themselves into the ASP.NET request processing pipeline by registering themselves for certain events. Whenever those events occur, ASP.NET invokes the interested HTTP modules so that the modules can play with the request.

An HTTP module is supposed to implement the following methods of the IHttpModule interface:

                       
  

Method Name
  

Description
  

Init
  

This method allows an HTTP module   to register its event handlers to the events in the HttpApplication object.
  

Dispose
  

This method gives HTTP module an   opportunity to perform any clean up before the object gets garbage collected.


An HTTP module can register for the following events exposed by the System.Web.HttpApplication object.

                                                                                                                       
  

Event Name
  

Description
  

AcquireRequestState
  

This event is raised when ASP.NET   runtime is ready to acquire the Session state of the current HTTP request.
  

AuthenticateRequest
  

This event is raised when ASP.NET   runtime is ready to authenticate the identity of the user.
  

AuthorizeRequest
  

This event is raised when ASP.NET   runtime is ready to authorize the user for the resources user is trying to   access.
  

BeginRequest
  

This event is raised when ASP.NET   runtime receives a new HTTP request.
  

Disposed
  

This event is raised when ASP.NET   completes the processing of HTTP request.
  

EndRequest
  

This event is raised just before   sending the response content to the client.
  

Error
  

This event is raised when an   unhandled exception occurs during the processing of HTTP request.
  

PostRequestHandlerExecute
  

This event is raised just after   HTTP handler finishes execution.
  

PreRequestHandlerExecute
  

This event is raised just before   ASP.NET begins executing a handler for the HTTP request. After this event,   ASP.NET will forward the request to the appropriate HTTP handler.
  

PreSendRequestContent
  

This event is raised just before   ASP.NET sends the response contents to the client. This event allows us to   change the contents before it gets delivered to the client. We can use this   event to add the contents, which are common in all pages, to the page output.   For example, a common menu, header or footer.
  

PreSendRequestHeaders
  

This event is raised just before   ASP.NET sends the HTTP response headers to the client. This event allows us   to change the headers before they get delivered to the client. We can use   this event to add cookies and custom data into headers.
  

ReleaseRequestState
  

This event is raised after ASP.NET   finishes executing all request handlers.
  

ResolveRequestCache
  

This event is raised to determine   whether the request can be fulfilled by returning the contents from the   Output Cache. This depends on how the Output Caching has been setup for your   web application.
  

UpdateRequestCache
  

This event is raised when ASP.NET   has completed processing the current HTTP request and the output contents are   ready to be added to the Output Cache. This depends on how the Output Caching   has been setup for your Web application.


Apart from these events, there are four more events that we can use. We can hook up to these events by implementing the methods in the global.asax file of our Web application.

These events are as follows:
  • Application_OnStart This event is raised when the very first request arrives to the Web      application.
  • Application_OnEnd This event is raised just before the application is going to terminate.
  • Session_OnStart This event is raised for the very first request of the user's session.
  • Session_OnEnd This event is raised when the session is abandoned or expired.

Registering HTTP Modules in Configuration Files

Once an HTTP module is built and copied into the bin directory of our Web application or copied into the Global Assembly Cache, then we will register it in either the web.config or machine.config file.

We can use <httpModules> and <add> nodes for adding HTTP modules to our Web applications. In fact the modules are listed by using <add> nodes in between <httpModules> and </httpModules> nodes.

Since configuration settings are inheritable, the child directories inherit configuration settings of the parent directory. As a consequence, child directories might inherit some unwanted HTTP modules as part of the parent configuration; therefore, we need a way to remove those unwanted modules. We can use the <remove> node for this.

If we want to remove all of the inherited HTTP modules from our application, we can use the <clear> node.

The following is a generic example of adding an HTTP module:

<httpModules>
  <add type="classname, assemblyname" name="modulename"  />
<httpModules>

The following is a generic example of removing an HTTP module from your application.

<httpModules>
   <remove name="modulename"  />
<httpModules>

In the above XML,
  • The type attribute specifies the actual type of the      HTTP module in the form of class and assembly name.
  • The name attribute specifies the friendly name for the      module. This is the name that will be used by other applications for    identifying the HTTP module.
Use of HTTP Modules by the ASP.NET Runtime

ASP.NET runtime uses HTTP modules for implementing some special features. The following snippet from the machine.config file shows the HTTP modules installed by the ASP.NET runtime.

<httpModules>
 <add name="OutputCache" type="System.Web.Caching.OutputCacheModule"/>
 <add name="Session" type="System.Web.SessionState.SessionStateModule"/>
 <add name="WindowsAuthentication"
   type="System.Web.Security.WindowsAuthenticationModule"/>
 <add name="FormsAuthentication"
   type="System.Web.Security.FormsAuthenticationModule"/>
 <add name="PassportAuthentication"
   type="System.Web.Security.PassportAuthenticationModule"/>
 <add name="UrlAuthorization"
   type="System.Web.Security.UrlAuthorizationModule"/>
 <add name="FileAuthorization"
   type="System.Web.Security.FileAuthorizationModule"/>
</httpModules>


All of the above HTTP modules are used by ASP.NET to provide services like authentication and authorization, session management and output caching. Since these modules have been registered in machine.config file, these modules are automatically available to all of the Web applications.




0 comments:

Post a Comment