Skip to content

Commit

Permalink
Restify support and httpError route.
Browse files Browse the repository at this point in the history
  • Loading branch information
Eric Elliott committed Oct 20, 2013
1 parent 5e534bd commit 54f04ed
Show file tree
Hide file tree
Showing 8 changed files with 201 additions and 17 deletions.
24 changes: 20 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ server.listen(port, function () {
});
```

## Configuration
## Configuration errorHandler(options)

Here are the parameters you can pass into the `errorHandler()` middleware:

Expand All @@ -62,7 +62,7 @@ Here are the parameters you can pass into the `errorHandler()` middleware:
See the tests for more examples.


## errorHandler.clientError()
## errorHandler.isClientError(status)

Return true if the error status represents a client error that should not trigger a restart.

Expand All @@ -73,8 +73,24 @@ Return true if the error status represents a client error that should not trigge
### Example

```js
errorHandler.clientError(404); // returns true
errorHandler.clientError(500); // returns false
errorHandler.isClientError(404); // returns true
errorHandler.isClientError(500); // returns false
```


## errorHandler.httpError(status, [message])

Take an error status and return a route that sends an error with the appropriate status and message to an error handler via `next(err)`.

* @param {number} status
* @param {string} message
* @return {function} Express route handler

```js
* // Define supported routes
* app.get( '/foo', handleFoo() );
* // 405 for unsupported methods.
* app.all( '/foo', createHandler.err(405) );
```

## Thanks
Expand Down
50 changes: 45 additions & 5 deletions error-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
var mixIn = require('mout/object/mixIn'),
path = require('path'),
fs = require('fs'),
statusCodes = require('http').STATUS_CODES,

/**
* Return true if the error status represents
Expand All @@ -27,7 +28,7 @@ var mixIn = require('mout/object/mixIn'),
* @param {number} status
* @return {boolean}
*/
clientError = function clientError(status) {
isClientError = function isClientError(status) {
return (status >= 400 && status <= 499);
},

Expand Down Expand Up @@ -64,6 +65,28 @@ var mixIn = require('mout/object/mixIn'),
exit(o);
},

/**
* Take an error status and return a route that
* sends an error with the appropriate status
* and message to an error handler via
* `next(err)`.
*
* @param {number} status
* @param {string} message
* @return {function} Express route handler
*/
httpError = function httpError (status, message) {
var err = new Error();
err.status = status;
err.message = message ||
statusCodes[status] ||
'Internal server error';

return function httpErr(req, res, next) {
next(err);
};
},

sendFile = function sendFile (staticFile, res) {
var filePath = path.resolve(staticFile),
stream = fs.createReadStream(filePath);
Expand Down Expand Up @@ -167,13 +190,12 @@ createHandler = function createHandler(options) {
if (defaultStatic) {
return sendFile(defaultStatic, res);
}

return res.send(status);
},

resumeOrClose = function
resumeOrClose(status) {
if (!clientError(status)) {
if (!isClientError(status)) {
return close(o, exit);
}
};
Expand Down Expand Up @@ -208,7 +230,7 @@ createHandler = function createHandler(options) {
// attackers can send malformed requests
// for the purpose of creating a Denial
// Of Service (DOS) attack.
if (clientError(status)) {
if (isClientError(status)) {
return renderDefault(status);
}

Expand All @@ -220,7 +242,25 @@ createHandler = function createHandler(options) {
};
};

createHandler.isClientError = isClientError;
createHandler.clientError = function () {
var args = [].slice.call(arguments);

console.log('WARNING: .clientError() is ' +
'deprecated. Use isClientError() instead.');

return this.isClientError.apply(this, args);
};

createHandler.restify = function restify(options) {
var handler = createHandler(options);
return function restifyHandler(req, res,
route, err) {
handler(err, req, res);
};
};

createHandler.clientError = clientError;
// HTTP error generating route.
createHandler.httpError = httpError;

module.exports = createHandler;
46 changes: 46 additions & 0 deletions examples/broken-restify.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use strict';

var restify = require('restify'),
server = restify.createServer(),

handleError = function handleError() {
console.log('Caught error!'); // never hserverens
setTimeout(function () {
process.exit(1);
}, 3000);
},

middlewareError =
function middlewareError() {
throw new Error('Random middleware error.');
};


server.get('/err', function (req, res, next) {
// This doesn't get caught.
next( new Error('Random unrecoverable error. ' +
'Server is now running in undefined state!') );
});

server.get('/thrower', function () {
// This doesn't get caught.
throw new Error('Random unrecoverable error. ' +
'Server is now running in undefined state!');
});

// This gets caught, yay!
server.use(middlewareError);

server.get('/middleware', function () {
// Placeholder to invoke middlewareError.
});

// This works for middleware. Fails for routes.
server.on('uncaughtException', handleError);

// Nope. This doesn't help.
process.on('uncaughtException', handleError);

server.listen(3000, function () {
console.log('Listening on port 3000');
});
2 changes: 2 additions & 0 deletions examples/error-only.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use strict';

var express = require('express'),
errorHandler = require('../error-handler.js'),
app = express(),
Expand Down
52 changes: 52 additions & 0 deletions examples/restify.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
'use strict';

var restify = require('restify'),
server = restify.createServer(),
errorHandler = require('../error-handler.js'),

handleError = errorHandler({
server: server
}),

middlewareError =
function middlewareError() {
throw new Error('Random middleware error.');
};


server.get('/err', function (req, res, next) {
// This doesn't get caught.
next( new Error('Random unrecoverable error. ' +
'Server is now running in undefined state!') );
});

server.get('/thrower', function () {
// This doesn't get caught.
throw new Error('Random unrecoverable error. ' +
'Server is now running in undefined state!');
});

// This gets caught, yay!
server.use(middlewareError);

server.get('/middleware', function () {
// Placeholder to invoke middlewareError.
});

handleError(
{req: true},
{
res: true,
send: function () {}
},
{route: true},
new Error('Testing handleError')
);

server.on('after', handleError);

process.on('uncaughtException', handleError);

server.listen(3000, function () {
console.log('Listening on port 3000');
});
4 changes: 2 additions & 2 deletions gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
module.exports = function(grunt) {
grunt.initConfig({
jshint: {
all: ['./test/**/*.js', './gruntfile.js',
'./error-handler.js'],
all: ['./gruntfile.js', './test/*.js',
'./examples/*.js', './error-handler.js'],
options: {
curly: true,
eqeqeq: true,
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "express-error-handler",
"version": "0.3.2",
"version": "0.4.0",
"description": "A graceful error handler for Express applications.",
"main": "error-handler.js",
"scripts": {
Expand Down Expand Up @@ -34,6 +34,7 @@
"grunt-contrib-jshint": "~0.6.4",
"bunyan-request-logger": "~0.1.1",
"connect-cache-control": "~0.1.0",
"through": "~2.3.4"
"through": "~2.3.4",
"restify": "~2.6.0"
}
}
35 changes: 31 additions & 4 deletions test/runtests.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,13 @@ test('Static file', function (t) {
handler(e, testReq(), res, testNext);
});

test('.clientError()', function (t) {
test('.isClientError()', function (t) {
var
serverPass = [399, 500].every(function (err) {
return !createHandler.clientError(err);
return !createHandler.isClientError(err);
}),
clientPass = [400, 401, 499].every(function(err) {
return createHandler.clientError(err);
return createHandler.isClientError(err);
});

t.ok(serverPass,
Expand Down Expand Up @@ -233,4 +233,31 @@ test('Default static file', function (t) {
});

handler(e, testReq(), res, testNext);
});
});

test('.restify()', function (t) {
var route,
shutdown = function shutdown() {
t.pass('Should return restify handler.');
t.end();
},

handler = createHandler
.restify({shutdown: shutdown});

// Restify uses a different signature:
handler(testReq(), testRes(), route, testError);
});

test('.create() http error handler', function (t) {
var next = function (err) {
t.equal(err.status, 405,
'Status message should be set on error.');
t.equal(err.message, 'Method Not Allowed',
'Should set message correctly.');
t.end();
},
handler = createHandler.httpError(405);

handler(null, null, next);
});

0 comments on commit 54f04ed

Please sign in to comment.