HttpHandlers are cool. There I said it. Not only are they cool but so incredibly easy to write. I've read about them, dabbled with them and even wrote so Haiku's about them - but never could I find a practical example to use them - until yesterday.
The Problem
So we have WebServer1 that is outside the firewall and is publicly and anonymously accessible via a URL. Queries made from WebServer1 for a specific image are passed through to WebServiceServer1 which resides behind the firewall and is accessible only through port 80 from WebServer1. The WebService on WebServiceServer1 makes a request to a running process that generates an image at runtime and returns a URL to the WebService, which then in turn returns it to the calling application on WebServer1. The problem is that this URL is not accessible from WebServer1, so how does the application on WebServer1 use this URL to render the image?
The Solution
It's true that the application on WebServer1 can't use the URL directly - but an application on WebServiceServer1 certainly can! The plan is to write an HttpHandler that uses the protected URL and streams the image back to the application outside the firewall. The following HttpHandler reads the querystring on the requested resource, looks for the key imageURL and streams that image resource back. The solution resides on the WebServiceServer1 server only and is requested by hitting an empty file which I happened to call GetImage.ashx, but you can call it anything as long as it matches your Web.Config entry.
For your test project, create an empty web project and take the code from Listing 1 and save it as HttpImageHandler.cs. Create an empty text file called GetImage.ashx and save it in your application folder as well. This file only serves as a placeholder so that IIS can attempt to serve this request. We are going to intercept the request for GetImage.ashx and parse out the querystring for the REAL resource. Finally add the following lines to your Web.Config inside the node. Basically the entry identifies that all requests (GET and POST) for the resource GetImage.ashx shall be processed by the ImageStream.HttpImageHandler class in the ImageStream.dll. If your compiled DLL is called something other than ImageStream, ensure that the type attribute reflects that.
<httpHandlers>
<add verb="*" path="GetImage.ashx" type="ImageStream.HttpImageHandler, ImageStream" />
< FONT>httpHandlers>
I landed up using the .ashx extension because it is already registered by .NET as a file type that is handled by the ASP.NET runtime. If you decide to use another file extension then you must ensure it is registered with IIS. http://www.uberasp.net/getarticle.aspx?id=13 has a great example of how to do this with IIS. In addition to the class below, you'll need an empty placeholder resource file that serves only as an entry point for requesting applications on WebServer1 into the protected server on WebServiceServer1.
So what does a request look like?
You can only fully test this by attempting to access an image that is behind a firewall but in practical terms you probably don't have that environment handy for playing around in, so you're going to have to trust me here. *wink*
I referenced the resource via the source attribute of an image on a web page.
img src=http://localhost/ImageStream/GetImage.ashx?imageUrl=http://i3.microsoft.com/h/en-us/i/READY_2_10.jpg
![]()
You should see the Microsoft Visual Studio logo in place on the page like so.

Listing 1
using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Net;
using System.Web;
namespace
ImageStream
{
public class HttpImageHandler : IHttpHandler
{
private string GetExtension(string url)
{
string extension = "";
if (null != url)
{
int indexSeperator = url.LastIndexOf(".");
if (indexSeperator > -1)
extension = url.Substring(indexSeperator + 1, url.Length - indexSeperator - 1).ToUpper();
}
return extension;
}
private string GetContentType(string url)
{
switch (GetExtension(url))
{
case "JPG" :
case "JPEG" :
return "image/jpg";
case "PNG" :
return "image/png";
case "GIF" :
return "image/gif";
default :
return "";
}
}
private ImageFormat GetImageFormat(string url)
{
switch (GetExtension(url))
{
case "JPG" :
case "JPEG" :
return ImageFormat.Jpeg;
case "PNG" :
return ImageFormat.Png;
case "GIF" :
return ImageFormat.Gif;
default :
return ImageFormat.Bmp;
}
}
private Image GetImage(string url)
{
HttpWebRequest wReq = (HttpWebRequest)WebRequest.Create(url);
HttpWebResponse wRes = (HttpWebResponse)(wReq).GetResponse();
Stream wStr = wRes.GetResponseStream();
return Image.FromStream(wStr);
}
#region
IHttpHandler Members
public void ProcessRequest(HttpContext context)
{
string url = context.Request.QueryString["imageUrl"];
if (null != url)
{
context.Response.Clear();
context.Response.ContentType = GetContentType(url);
Image img = GetImage(url);
img.Save(context.Response.OutputStream, GetImageFormat(url));
}
context.Response.End();
}
public bool IsReusable
{
get { return false; }
}
#endregion
}
}