Skip to content

Commit

Permalink
fix headersSent check
Browse files Browse the repository at this point in the history
This issue was discovered when an over-limit request body is
sent to LB4. Destroying the request socket causing the client
to fail instead of receiving a meaningful error.
  • Loading branch information
raymondfeng committed Oct 17, 2018
1 parent e7aa835 commit 26e8f8f
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 5 deletions.
3 changes: 2 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@ declare namespace errorHandlerFactory {
* @param req Incoming request
* @param res Response
* @param options Options for error handler settings
* @returns false if the response has been sent before the error
*/
function writeErrorToResponse(
err: Error,
req: Express.Request,
res: Express.Response,
options?: ErrorWriterOptions
): void;
): boolean;

/**
* Error-handling middleware function. Includes server-side logging
Expand Down
11 changes: 7 additions & 4 deletions lib/handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ var SG = require('strong-globalize');
SG.SetRootDir(path.resolve(__dirname, '..'));
var buildResponseData = require('./data-builder');
var debug = require('debug')('strong-error-handler');
var format = require('util').format;
var logToConsole = require('./logger');
var negotiateContentProducer = require('./content-negotiation');

Expand Down Expand Up @@ -50,9 +49,12 @@ function writeErrorToResponse(err, req, res, options) {

options = options || {};

if (res._header) {
debug('Response was already sent, closing the underlying connection');
return req.socket.destroy();
// See https://nodejs.org/api/http.html#http_response_headerssent
if (res.headersSent) {
debug('Response was already sent. Skipping error response.');
// We should not destroy the request socket as it causes Error: write EPIPE
// return req.socket.destroy();
return false;
}

// this will alter the err object, to handle when res.statusCode is an error
Expand All @@ -67,6 +69,7 @@ function writeErrorToResponse(err, req, res, options) {

var sendResponse = negotiateContentProducer(req, warn, options);
sendResponse(res, data);
return true;

function warn(msg) {
res.header('X-Warning', msg);
Expand Down
23 changes: 23 additions & 0 deletions test/handler.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,24 @@ describe('strong-error-handler', function() {
request.get('/').expect(200, 'empty', done);
});

it('handles response headers already sent without destroying the request',
function(done) {
givenErrorHandlerForError();
var handler = _requestHandler;
_requestHandler = function(req, res, next) {
res.end('empty');
process.nextTick(function() {
handler(req, res, next);
});
};
request.post('/').send(givenLargeRequest())
.expect(200, 'empty', function(err) {
// Skip EPIPE
if (err && err.code !== 'EPIPE') return done(err);
else return done();
});
});

context('status code', function() {
it('converts non-error "err.status" to 500', function(done) {
givenErrorHandlerForError(new ErrorWithProps({status: 200}));
Expand Down Expand Up @@ -919,3 +937,8 @@ function getExpectedErrorData(err) {
cloneAllProperties(data, err);
return data;
}

function givenLargeRequest() {
const data = Buffer.alloc(2 * 1024 * 1024, 'A', 'utf-8');
return data.toString();
}

0 comments on commit 26e8f8f

Please sign in to comment.