Tuesday, September 4, 2007

Samples: Change Case

This sample attempts to demonstrate a managed view (although a bit modified) of the native sample UpCase Microsoft has provided some time ago to uppercase the payload.

Its simple to see how easy it is to do the same in managed code and at the same time enable this same behavior to any page you request. The sample here allows to uppercase or lowercase the payload of any http response by adding /uc or /lc to the url requested, respectively.



using System;
using System.Collections.Generic;
using System.Text;
using KodeIT.Web;

namespace FilterDotNet.Samples.ChangeCase
{
internal class Filter : IHttpFilter
{
void IHttpFilter.Dispose()
{
}

void IHttpFilter.Init(IFilterEvents events)
{
events.PreProcHeaders += new EventHandler(OnPreProcHeaders);
events.SendRawData += new EventHandler(OnSendRawData);
}

void OnPreProcHeaders(object sender, PreProcHeadersEventArgs e)
{
e.Context.Session.Clear();

if (e.Context.Url.ToLower().EndsWith("/uc"))
{
e.Context.Session["UpCase"] = true;
e.Context.Url = e.Context.Url.Substring(0, e.Context.Url.Length - 3);
}
else if (e.Context.Url.ToLower().EndsWith("/lc"))
{
e.Context.Session["UpCase"] = false;
e.Context.Url = e.Context.Url.Substring(0, e.Context.Url.Length - 3);
}
}

void OnSendRawData(object sender, RawDataEventArgs e)
{
if (e.Context.Session.ContainsKey("UpCase"))
{
bool upCase = (bool)e.Context.Session["UpCase"];
int streamIndex = 0;
byte[] bytes = e.Context.GetData();

if (!e.Context.Session.ContainsKey("Headers"))
{
for (int ix = 0; ix < (bytes.Length - 2); ix++)
{
if ((bytes[ix + 0] == 0x0D) && (bytes[ix + 1] == 0x0A) &&
(bytes[ix + 2] == 0x0D) && (bytes[ix + 3] == 0x0A))
{
e.Context.Session["Headers"] = null;
streamIndex = ix + 4;
break;
}
}
}

if (e.Context.Session.ContainsKey("Headers"))
{
if (upCase)
{
for (; streamIndex < bytes.Length; streamIndex++)
{
byte b = bytes[streamIndex];
if ((b >= 'a') && (b <= 'z'))
{
bytes[streamIndex] = (byte)((b - 'a') + 'A');
}
}
}
else
{
for (; streamIndex < bytes.Length; streamIndex++)
{
byte b = bytes[streamIndex];
if ((b >= 'A') && (b <= 'Z'))
{
bytes[streamIndex] = (byte)((b - 'A') + 'a');
}
}
}

e.Context.SetData(bytes);
}
}
}
}
}


By carefully looking at the code, we can see that the SendRawData event finds the end of the http headers before starting the conversion process. The PreProcHeaders event is used to signal if the conversion should be made to uppercase, lowercase or not be done at all. The Url is adjusted afterwards if needed.

2 comments:

Nuno said...

Excellent work. I always thought you needed to get dirty with C++ to do this kinda stuff. Congratulations.

Tiago Halm said...

Thats the main purpose, to ease the authoring of ISAPI Filters, basically allowing the .NET developer to extend IIS in a similar way to HttpModules in the ASP.NET framework.

I'm glad this framework is being put to good use.