Created
January 23, 2011 16:36
-
-
Save stellaraccident/792197 to your computer and use it in GitHub Desktop.
Illustrate exception handling with a simple proxy middleware
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Pattern for performing error handling given standard node | |
* callback patterns (ie. error passed as first argument). | |
*/ | |
function Armorer(nextCallback) { | |
this.nextCallback=nextCallback; | |
this.cleanupCallbacks=[]; | |
} | |
Armorer.prototype={ | |
onCleanup: function(callback) { | |
this.cleanupCallbacks.push(callback); | |
return this; | |
}, | |
/** | |
* Given an eventEmitter, attach to the error event such that | |
* the retreat is called if it is ever raised. | |
*/ | |
enlist: function(eventEmitter, cleanupCallback) { | |
eventEmitter.on('error', this.retreat.bind(this)); | |
if (cleanupCallback) { | |
this.cleanupCallbacks.push(cleanupCallback); | |
} | |
return this; | |
}, | |
/** | |
* Handle the given exception, invoking all exception handlers | |
* in the process. | |
*/ | |
retreat: function(exception) { | |
this.cleanup(); | |
return this.nextCallback(exception); | |
}, | |
/** | |
* Initiate cleanup | |
*/ | |
cleanup: function() { | |
for (var i=0; i<this.cleanupCallbacks.length; i++) { | |
var cb=this.cleanupCallbacks[i]; | |
cb(); | |
} | |
}, | |
/** | |
* Traps any exceptions coming out of callback and calls | |
* retreat() if they happen. | |
*/ | |
guard: function(callback) { | |
try { | |
return callback(); | |
} catch (e) { | |
console.log('Exception handled in guarded callback: ' + e); | |
return this.retreat(e); | |
} | |
}, | |
/** | |
* Returns a function that will invoke callback with full exception | |
* handling. | |
*/ | |
trap: function(callback) { | |
var me=this; | |
return function() { | |
try { | |
return callback.apply(this, arguments); | |
} catch (e) { | |
console.log('Exception handled in guarded callback: ' + e); | |
return me.retreat(e); | |
} | |
}; | |
} | |
}; | |
function armor(nextCallback) { | |
return new Armorer(nextCallback); | |
} | |
Function.prototype.armor=function(armorer) { | |
return armorer.trap(this); | |
}; | |
module.exports=armor; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Simple proxy intended to be used as middleware to direct parts | |
* of an HTTP namespace to other servers. | |
* | |
* Copyright (c) 2011, Stella Laurenzo | |
*/ | |
var util=require('util'), | |
url=require('url'), | |
http=require('http'), | |
armor=require('./armor'); | |
function makeAppendDestination(baseUrl) { | |
return function(request) { | |
//console.log('Request: ' + util.inspect(request)); | |
return baseUrl + request.url; | |
}; | |
} | |
module.exports=function(destination) { | |
if (typeof destination==='string') | |
destination=makeAppendDestination(destination); | |
return function(req, res, next) { | |
var newUrl=destination(req), | |
parsed=url.parse(newUrl), | |
port=parsed.port; | |
//console.log('url: ' + util.inspect(parsed)); | |
if (!port) { | |
if (parsed.protocol==='http:') port=80; | |
else if (parsed.protocol==='https:') port=443; | |
else port=80; | |
} | |
var client=http.createClient(port, parsed.hostname), | |
clientRequest=client.request(req.method, | |
parsed.pathname + (parsed.search||''), | |
req.headers); | |
// Error handling | |
var a=armor(next) | |
.enlist(client) | |
.enlist(clientRequest, clientRequest.end.bind(clientRequest)); | |
// Patch through the response | |
clientRequest.on('response', function(pxres) { | |
pxres.on('data', function(chunk) { | |
res.write(chunk, 'binary'); | |
}.armor(a)); | |
pxres.on('end', function() { | |
res.end(); | |
}.armor(a)); | |
res.writeHead(pxres.statusCode, pxres.headers); | |
}.armor(a)); | |
// Patch through the request | |
req.on('data', function(chunk) { | |
clientRequest.write(chunk, 'binary'); | |
}.armor(a)); | |
req.on('end', function() { | |
clientRequest.end(); | |
}.armor(a)); | |
}; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment