Let's examine how Microsoft's most popular web server parses
Http requests. When Internet Information Services (IIS) receives an Http
request, it parses the extension of the file targeted by the URL. Usually, file
extensions are associated with assemblies containing methods and functions that
will process requests. Mappings from file extensions to assemblies are saved in
the IIS metabase. When ASP.NET is installed, it adds entries to the metabase
associating some file extensions including .aspx and .asmx with the "aspnet_isapi.dll"
assembly file.
The "HTTP_REFERER" server variable can tell when a
request is made from another website. If someone puts the URL of an image, for
example, into a web browser, or follows a direct link from an email, there
won't be a referrer.
In .NET, an Http Handler class should implement the "IHttpHandler"
interface as per the following code listing.
To go in tandem with this article's contents, it is recommended
that you try to implement and test an Http Handler yourself. It's an easy task.
Start by opening Visual Studio .NET. Create a Class Library project and name it
"HttpHandlersLibrary." Delete the "Class1.vb" (or
"Class1.cs") file created by default and create a class called
"FileTypeHandler.vb" (or "FileTypeHandler.cs"). Replace the
contents of the already created class by the contents of the following listing
and rebuild the project. In the bin folder, the "HttpHandlersLibrary.dll"
assembly file will be created.
Listing 1 - VB Code
Imports System
Imports System.Web
Public Class FileTypeHandler
Implements IHttpHandler
'' The ProcessRequest method decides what to do with the request.
Public Sub ProcessRequest(ByVal Context As System.Web.HttpContext)_
Implements System.Web.IHttpHandler.ProcessRequest
If Context.Request.ServerVariables("HTTP_REFERER") Is Nothing Then Exit Sub
'' strFileName holds the full path of the request,
''for example http://domain/folder/page.aspx
Dim strFileName As String = Context.Server.MapPath(Context.Request.FilePath)
Dim cfg As New System.Configuration.AppSettingsReader
'' The strDomainNameCollection is an array of comma-separated
'' values holding the address(es) of your website.
Dim strDomainNameCollection As String() = CType(cfg.GetValue("DomainName", _
GetType(System.String)), String).ToLower.Split(",")
'' Now we check if the Http Referrer is listed in strDomainNameCollection.
If Array.IndexOf(strDomainNameCollection, _
Context.Request.ServerVariables("HTTP_REFERER").ToLower.Split("/")(2)) > -1 Then
Select Case strFileName.Split(".")(strFileName.Split(".").Length - 1).ToLower
Case "jpg", "jpeg"
Context.Response.ContentType = "image/JPEG"
Context.Response.WriteFile(strFileName)
Case "gif"
Context.Response.ContentType = "image/GIF"
Context.Response.WriteFile(strFileName)
Case "js"
Context.Response.ContentType = "text/javascript"
Context.Response.WriteFile(strFileName)
Case "css"
Context.Response.ContentType = "text/css"
Context.Response.WriteFile(strFileName)
Case Else
End Select
End If
End Sub
'' This property tells the server whether or not the handler object can be reused.
Public ReadOnly Property IsReusable() As Boolean _
Implements System.Web.IHttpHandler.IsReusable
Get
Return True
End Get
End Property
End Class
Listing 2 - C# Code
using System;
using System.Web;
namespace HttpHandlersLibrary
{
public class FileTypeHandler: IHttpHandler
{
/// The ProcessRequest method decides what to do with the request.
public void ProcessRequest(System.Web.HttpContext Context)
{
if (Context.Request.ServerVariables["HTTP_REFERER"] == null)
{
return ;
}
/*strFileName holds the full path of the request, for example
http://domain/folder/page.aspx*/
string strFileName = Context.Server.MapPath(Context.Request.FilePath);
System.Configuration.AppSettingsReader cfg = new
System.Configuration.AppSettingsReader();
/// The strDomainNameCollection is an array of comma-separated
/// values holding the address(es) of your website.
string[]strDomainNameCollection = System.Convert.ToString(cfg.GetValue(
"DomainName", typeof(System.String))).ToLower().Split(Convert.ToChar(
","));
/// Now we check if the Http Referrer is listed in strDomainNameCollection.
if (Array.IndexOf(strDomainNameCollection,
Context.Request.ServerVariables["HTTP_REFERER"].ToLower().Split
(Convert.ToChar("/"))[2]) > - 1)
{
switch (strFileName.Split(Convert.ToChar("."))[strFileName.Split
(Convert.ToChar(".")).Length - 1].ToLower())
{
case "jpg":
Context.Response.ContentType = "image/JPEG";
Context.Response.WriteFile(strFileName);
break;
case "jpeg":
Context.Response.ContentType = "image/JPEG";
Context.Response.WriteFile(strFileName);
break;
case "gif":
Context.Response.ContentType = "image/GIF";
Context.Response.WriteFile(strFileName);
break;
case "js":
Context.Response.ContentType = "text/javascript";
Context.Response.WriteFile(strFileName);
break;
case "css":
Context.Response.ContentType = "text/css";
Context.Response.WriteFile(strFileName);
break;
default:
break;
}
}
}
/* This property tells the server whether or not the handler object can be reused.*/
public bool IsReusable
{
get
{
return true;
}
}
}
}
Now use Visual Studio .NET to create an ASP.NET Web
Application project in the following location http://localhost/TestingHttpHandlers.
Delete the "WebForm1.aspx" file and create a Web Page called "Default.aspx."
Drag and drop into this page a .jpg or .jpeg or .gif picture that you've
already included in the solution.
Now you need to map the file extensions to the new Http
Handler(s) assembly(ies) in IIS. For this purpose, go to the Internet
Information Services manager console (you can do this by typing in your Run
command "inetmgr.msc" without the quotes), right click the already
created website (or virtual directory), select the "Home Directory"
(or "Virtual Directory") tab, click the "Configuration"
button (see Figure 1) and select the "Mappings" tab in the pop-up,
click the "Add" button (see Figure 2) and browse to select the "HttpHandlersLibrary.dll"
file previously created, input ".jpg" (without the quotes) in the "Extension"
field and click the "OK" button (see Figure 3). Follow the same way
to add mappings for .jpeg, .gif, .css, and .js extensions.
Figure 1
Figure 2
Figure 3
One more thing to do is to add the "HttpHandlers"
entry in the Web.config file. This informs the .NET Framework about the
mappings between handlers and file extensions. The same way we did above to map
file extensions to assemblies in IIS, we need to map file extensions to handler
classes since the "HttpHandlersLibrary.dll" assembly can hold
multiple classes implementing the IHttpHandler interface.
Listing 3
<configuration>
<system.web>
<HttpHandlers>
<add verb="*"
path="*.jpg" type="HttpHandlersLibrary.FileTypeHandler, HttpHandlersLibrary"/>
<add verb="*"
path="*.jpeg" type="HttpHandlersLibrary.FileTypeHandler, HttpHandlersLibrary"/>
<add verb="*"
path="*.gif" type="HttpHandlersLibrary.FileTypeHandler, HttpHandlersLibrary"/>
<add verb="*"
path="*.js" type="HttpHandlersLibrary.FileTypeHandler, HttpHandlersLibrary"/>
<add verb="*"
path="*.css" type="HttpHandlersLibrary.FileTypeHandler, HttpHandlersLibrary"/>
</HttpHandlers>
</system.web>
<appSettings>
<add key="DomainName"
value="127.0.0.1,localhost"/>
</appSettings>
</configuration>
If you're using Visual Studio .NET 2003 make sure to write
"httpHandlers" with a lower case starting
"h" character.
You need to add for each file extension a sub-entry in the
"HttpHandlers" section. The path contains the extensions each
preceded by a wildcard. The type has the following format
"NamespaceName.ClassName, AssemblyName." Make sure to respect the
casing in "Web.config" since it's a case-sensitive file.
Note the "appSettings" section of the
"Web.config" file, I have included in it the domain address(es)
considered as trusted.
Now it's time to test what you've done so far.
Run the web application locally, you'll see that the image
will show up in the page. If you now try to access the image directly through http://localhost/TestingHttpHandlers/ImageName.jpg,
you'll get a blank page since the "HTTP_REFERER" server variable is
not defined. Same thing will happen if you try to directly access this image
from another computer since the "HTTP_REFERER" will hold the address
of the latter computer which is not listing in the "DomainName" key
in the "appSettings" section.