I often try to learn something interesting (mostly programming related) whenever I get a long break from work. Last Thanksgiving, I wrote an iPhone app that syncs photo among S3, Flickr and Facebook. This Xmas, I took on writing my first node.js app.
I used http://www.nodebeginner.org/ as a starting point. 5 minutes into the tutorial, I encountered a strange problem. For every request sent from chrome browser to my node.js server, my service recorded TWO requests. A quick google search indicated that chrome ALWAYS sends an additional "/favicon.ico" request to an HTTP server if it cannot locate an icon for that server.
This was really annoying because it messed my global debugging counter. The solution was simple: just ignore all requests in the format of "/favicon.ico". But "SIMPLE" solutions are no fun, especially during learning process. If I were to do this in java, I'd use ServletFilter to "preFilter" out all unqualified requests. So I put my javascript and java skills to the test, and wrote this simple FilterChain function in node.js. Enjoy!
var http = require("http");
/*
* Manage all filters.
*/
var filterChain = {
filters: new Array(),
add: function(filter) {
this.filters.push(filter);
},
applyAll: function(request, response) {
this.apply(request, response, 0);
},
apply: function(request, response, i) {
if (i == this.filters.length) {
return processRequest(request, response);
}
var filter = this.filters[i];
// call preFilter and exits if fails
console.log(filter.name + ".preFilter");
success = filter.preFilter(request, response);
if (!success) {
return false;
}
// call next filter and exits if fails
success = this.apply(request, response, i+1);
if (!success) {
return false;
}
// call postFilter and exits
console.log(filter.name + ".postFilter");
success = filter.postFilter(request, response);
return success;
}
}
/*
* Filters
* All filters must implement 3 things:
* name - String unique name for this filter
* preFilter() - Executed before processing request
* postFilter() - Executed after processing request
*/
var faviconFilter = {
name: "favicon",
preFilter: function(request, response) {
if (request.url === '/favicon.ico') {
response.writeHead(200, {'Content-Type': 'image/x-icon'} );
response.end();
console.log('favicon request, filtered out!');
return false;
} else {
return true;
}
},
postFilter: function(request, response){return true;}
};
var latencyFilter = {
name: "latency",
timer: null,
preFilter: function(request, response) {
this.timer = process.hrtime();
return true;
},
postFilter: function(request, response) {
diff = process.hrtime(this.timer);
console.log("<%s>%ds%dns", request.url, diff[0], diff[1]);
return true;
}
};
filterChain.add(latencyFilter);
filterChain.add(faviconFilter);
/*
* This is the actual function that processes the request
*/
function processRequest(request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
console.log("Response send.");
response.end();
return true;
}
function onRequest(request, response) {
filterChain.applyAll(request, response);
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
No comments:
Post a Comment