My Resume

  • My Resume (MS Word) My Resume (PDF)


Affiliations

  • Microsoft Most Valuable Professional
  • INETA Community Champion
  • Leader, NJDOTNET: Central New Jersey .NET User Group

Sunday, April 19, 2009

Of HTTP Headers and Firefox Add-Ons…

Until just last week we had hosted our main website on a single web server, however an impressive team effort of just a few days brought that situation to a screeching halt when we finally moved everything to a brand new, long-awaited web farm. The benefits of web farms are both well-known and mostly obvious, but it’s not all roses and puppy dogs - web farms certainly bring their share of headaches as well.

One of the pains I immediately felt as soon as we went live was the fact that I had no idea which server in the farm I was hitting with any given request. Yes, of course to you the site user this really shouldn’t (and doesn’t) matter, but for me and my team when something isn’t going quite right, knowing which machine this uninvited behavior is originating from is a crucial piece of information. My knee-jerk reaction was to place some kind of identifier in the page content. To this end, I opened up the master pages on each server, scrolled to the end and plopped in an HTML comment “<!-- A -->”, “<!-- B -->”, and so on. This certainly fit my immediate requirement, but had a few annoying downsides: first off, this certainly wasn’t a maintainable solution, since it would have to be re-applied (probably manually, on every server) with every release and – if that weren’t enough by itself (which it was!) – it was just plain annoying having to right-click, View Source, scroll to the bottom, and search for this identifier. My immediate need was filled, but the geek in me was raging – there had to be a better way!

Take 2: After a bit of consideration, I thought taking advantage of the Footers feature in IIS might be a good idea. I’d never used them before, and didn’t know how well it would work for our goals, but decided to try it out anyway. “Open IIS Management console, right-click on my site, Properties… wait – the HTTP Headers tab!” Most any web developer who’s done a decent bit of AJAX has probably used – or at least seen usage of – custom HTTP Headers as a way of communicating meta-data about web responses (e.g. the “X-MicrosoftAjax” header used in ASP.NET AJAX to identify an AJAX request). Frustrated with myself that I hadn’t thought about this initially, I realized this is exactly where I’d wanted to put this info all along. My initial solution of putting the HTML comment in the text left a bad taste in my mouth for so many reasons, but at this point I realized that the worst part about it was that I was putting response metadata in the content, thus committing a blatant violation of Separating Concerns. After slapping my wrist with a ruler and vowing never to do that again (yeah, right!), I set forth to correct it, quickly adding a new custom HTTP Header called “X-ResponseOrigin” on each of my servers, and each with its own unique value. After going back and removing those silly HTML comments, I sat back, made a few requests and happily viewed the responses coming back in Firebug, knowing exactly which server had produced each one.

So at this point I’m pretty pleased with the way things are going. With Firebug open, I could see everything I needed to in order to troubleshoot server-specific issues. But, after a short time I started getting annoyed again. With every new request, I’d have to scroll down to the bottom of the request history in Firebug, find my request, expand it out, and scroll down, all the while scanning for my new special header. Trying to find this new piece of metadata I added – while arguably better than the previous “View Source, Scroll ‘n Scan” method – was still pretty darn annoying… oh, and what about the guys on my team who didn’t have Firebug?? (NOTE: this is really a hypothetical situation, since it is a well-known team rule that not having Firebug on your machine - even if you’re a non-developer – is punishable by 30 push-ups and having to buy everyone donuts the following morning.) It’s at this point that I remembered what Firebug was to begin with – a Firefox Addon – and realized what I must do next…

Enter the Firefox Addon

I needed to make a custom Firefox Addon to show me what server I was on, right there in my Firefox statusbar. No, it wasn’t a requirement for this project – it was utterly imperative to fulfilling my role of “Lead Code Monkey”; it was my destiny.

A quick Googling brought me to the Mozilla Developer portal, with its wealth of knowledge of all things Mozilla. Come to find out, writing a Firefox Addon is as simple as some XHTML/XML-ish markup and accompanying JavaScript – what could be easier!? The markup was quick – I just had to declare that I wanted a custom statusbarpanel that I could put my content in. The resulting code looked like this:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="chrome://whereami/skin/overlay.css" type="text/css"?>
<!DOCTYPE overlay SYSTEM "chrome://whereami/locale/whereami.dtd">
<overlay id="whereami-overlay"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script src="overlay.js"/>
<stringbundleset id="stringbundleset">
<stringbundle id="whereami-strings" src="chrome://whereami/locale/whereami.properties"/>
</stringbundleset>
<statusbar id="status-bar">
<statusbarpanel id="whereami.location" label="" />
</statusbar>
</overlay>




As you can see, it’s pretty straightforward. We’ve got our standard XML cruft, followed by what looks like a typical JavaScript reference tag, pointing to our script file (which we’ll get to in just a minute). This is followed by our <statusbar> and <statusbarpanel> element, letting Firefox know that all we’re going to need for our UI is a little piece of the existing status bar. We also gave that statusbarpanel an ID so we can easily reference it from our scripts later on. Actually, forget “later on” – let’s go see what those scripts look like!



Before we see the code behind all this, let’s revisit the requirements. Basically, we’re look for this add-on to do two things: get our custom HTTP header from requests that come in, and (if we find one) display it in the browser’s status bar. Initially, I had expected the latter to be the difficult part, but much to my surprise it turned out to be as simple as this one line:



document.getElementById(‘whereami.location’).label = responseOrigin;



The cool part here is the usage of the “document.getElementById()” DOM searching that you’re already used to from your normal JavaScript forays. And, there’s that element ID “whereami.location” that we set earlier in our overlay.xul file. Now that we’ve got the statusbar update figured out, let’s populate the value of responseOrigin



Quick Intro to the XPCOM API


In order to get a chance to look at what the user is browsing, you’ve got to create a class that you can register with Firefox as a listener. From that point, you’ll be notified whenever a browsing event happens – say, when a new response has been received. Before we look at the actual implementation, let’s look at a basic implementation of a Firefox listener class (as taken from the Mozilla Developer Center):



function myObserver()
{
this.register();
}
myObserver.prototype = {
observe: function(subject, topic, data) {
// Do your stuff here.
},
register: function() {
var observerService = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
observerService.addObserver(this, "myTopicID", false);
},
unregister: function() {
var observerService = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
observerService.removeObserver(this, "myTopicID");
}
}




You’ll see, all the magic happens in the observe() method, which gets fired whenever a browser event happens. For the purposes of this app, we’re looking out for any time an http-on-examine-response event is fired, indicating a new response has been received. That’s pretty easy, we’ll just check the value of eventName parameter:



if (topic == "http-on-examine-response") { /* Grab the Response Origin */ }



Now let’s take a look at my implementation:



var whereami = {
requestObserver:
{
isRegistered: false,

observe: function(subject, topic, data)
{
if (topic == "http-on-examine-response") {
var statusBar = document.getElementById('whereami.location');
statusBar.label = "";
var httpChannel = subject.QueryInterface(Components.interfaces.nsIHttpChannel);
var origin = httpChannel.getResponseHeader("X-ResponseOrigin");
if(origin && origin.length > 0)
origin = "Server: " + origin;
statusBar.label = origin;
}
},

get observerService() {
return Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
},

register: function()
{
if(this.isRegistered) return;

this.observerService.addObserver(this, "http-on-examine-response", false);
this.isRegistered = true;
},

unregister: function()
{
if(!this.isRegistered) return;

this.observerService.removeObserver(this, "http-on-examine-response");
this.isRegistered = false;
}
}
};

window.addEventListener("load", function(e) { whereami.requestObserver.register(); }, false);










You can see this is basically the same as the snippet from Mozilla, but I’ve added my logic right into the observe() method (lines 8-16).  Let’s see what we’re doing here:




  1. First, we have to get a reference to our little section of the statusbar that we reserved via the ID that I gave it earlier in the overlay.xul markup, clearing any existing (stale) data (lines 9 & 10)


  2. Then, we examine the HTTP headers that got sent back in our response, looking for the value of the “X-ResponseOrigin” that was sent from the server.  (lines 11-13)


  3. Finally, we’ll update the statusbar label with the value we got from Step 2  (line 15)

0 comments: