Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: properly handle non-errors thrown in domains #1757

Merged
merged 3 commits into from Mar 16, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 9 additions & 4 deletions lib/server.js
Expand Up @@ -1303,19 +1303,24 @@ Server.prototype._routeErrorResponse = function _routeErrorResponse(
return;
}

self._emitErrorEvents(req, res, null, err, function emitError(emitErr) {
self._emitErrorEvents(req, res, null, err, function emitError() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happened to emitErr?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked at _emitErrorEvents, and emitErr is actually the same object as err, just passed back. It seemed confusing as heck, so I removed it outright. In retrospect, I should probably remove calling this callback w/ err to begin with.

// Prevent double handling
if (res._sent) {
return;
}

// Expose only knonw errors
if (_.isNumber(err.statusCode)) {
// only automatically send errors that are known (e.g., restify-errors)
if (err instanceof Error && _.isNumber(err.statusCode)) {
res.send(err);
return;
}

res.send(new errors.InternalError(emitErr || err));
// if the thrown exception is not really an Error object, e.g.,
// "throw 'foo';"
// try to do best effort here to pass on that value by casting it to a
// string. This should work even for falsy values like 0, false, null,
// or undefined.
res.send(new errors.InternalError(String(err)));
});
};

Expand Down
132 changes: 129 additions & 3 deletions test/server.test.js
Expand Up @@ -2482,6 +2482,37 @@ test('uncaughtException should not trigger named routeHandler', function(t) {
});
});

test('uncaughtException should handle thrown null', function(t) {
SERVER.get(
{
name: 'foo',
path: '/foo'
},
function(req, res, next) {
throw null; //eslint-disable-line no-throw-literal
}
);

SERVER.get(
{
name: 'bar',
path: '/bar'
},
function(req, res, next) {
// This code should not run, but we can test against the status code
res.send(200);
next();
}
);

CLIENT.get('/foo', function(err, _, res, data) {
t.ok(err);
t.equal(res.statusCode, 500);
t.equal(data.message, 'null');
t.end();
});
});

test('uncaughtException should handle thrown undefined literal', function(t) {
SERVER.get(
{
Expand All @@ -2505,14 +2536,46 @@ test('uncaughtException should handle thrown undefined literal', function(t) {
}
);

CLIENT.get('/foo', function(err, _, res) {
CLIENT.get('/foo', function(err, _, res, data) {
t.ok(err);
t.equal(res.statusCode, 500);
t.equal(data.message, 'undefined');
t.end();
});
});

test('uncaughtException should handle thrown falsy number', function(t) {
SERVER.get(
{
name: 'foo',
path: '/foo'
},
function(req, res, next) {
throw 0; //eslint-disable-line no-throw-literal
}
);

SERVER.get(
{
name: 'bar',
path: '/bar'
},
function(req, res, next) {
// This code should not run, but we can test against the status code
res.send(200);
next();
}
);

CLIENT.get('/foo', function(err, _, res, data) {
t.ok(err);
t.equal(data.message, '0');
t.equal(res.statusCode, 500);
t.end();
});
});

test('uncaughtException should handle thrown Number', function(t) {
test('uncaughtException should handle thrown non falsy number', function(t) {
SERVER.get(
{
name: 'foo',
Expand All @@ -2535,8 +2598,71 @@ test('uncaughtException should handle thrown Number', function(t) {
}
);

CLIENT.get('/foo', function(err, _, res) {
CLIENT.get('/foo', function(err, _, res, data) {
t.ok(err);
t.equal(data.message, '1');
t.equal(res.statusCode, 500);
t.end();
});
});

test('uncaughtException should handle thrown boolean', function(t) {
SERVER.get(
{
name: 'foo',
path: '/foo'
},
function(req, res, next) {
throw true; //eslint-disable-line no-throw-literal
}
);

SERVER.get(
{
name: 'bar',
path: '/bar'
},
function(req, res, next) {
// This code should not run, but we can test against the status code
res.send(200);
next();
}
);

CLIENT.get('/foo', function(err, _, res, data) {
t.ok(err);
t.equal(data.message, 'true');
t.equal(res.statusCode, 500);
t.end();
});
});

test('uncaughtException should handle thrown falsy boolean', function(t) {
SERVER.get(
{
name: 'foo',
path: '/foo'
},
function(req, res, next) {
throw false; //eslint-disable-line no-throw-literal
}
);

SERVER.get(
{
name: 'bar',
path: '/bar'
},
function(req, res, next) {
// This code should not run, but we can test against the status code
res.send(200);
next();
}
);

CLIENT.get('/foo', function(err, _, res, data) {
t.ok(err);
t.equal(data.message, 'false');
t.equal(res.statusCode, 500);
t.end();
});
Expand Down