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: bodyReader works with gunzip and maxBodySize #1787

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
28 changes: 26 additions & 2 deletions lib/plugins/bodyReader.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ var BadDigestError = errors.BadDigestError;
var RequestEntityTooLargeError = errors.RequestEntityTooLargeError;
var PayloadTooLargeError = errors.PayloadTooLargeError;
var UnsupportedMediaTypeError = errors.UnsupportedMediaTypeError;
var BadRequestError = errors.BadRequestError;
var InternalServerError = errors.InternalServerError;

var MD5_MSG = "Content-MD5 '%s' didn't match '%s'";

Expand Down Expand Up @@ -96,12 +98,16 @@ function bodyReader(options) {
var md5;

var unsupportedContentEncoding;
var gzError;

if ((md5 = req.headers['content-md5'])) {
hash = crypto.createHash('md5');
}

function done() {
var err;
var msg;

bodyWriter.end();

if (unsupportedContentEncoding) {
Expand All @@ -119,8 +125,7 @@ function bodyReader(options) {
}

if (maxBodySize && bytesReceived > maxBodySize) {
var msg = 'Request body size exceeds ' + maxBodySize;
var err;
msg = 'Request body size exceeds ' + maxBodySize;

// Between Node 0.12 and 4 http status code messages changed
// RequestEntityTooLarge was changed to PayloadTooLarge
Expand All @@ -135,6 +140,21 @@ function bodyReader(options) {
return;
}

if (gzError) {
if (
gzError.errno === zlib.Z_DATA_ERROR ||
gzError.errno === zlib.Z_STREAM_ERROR
) {
msg = 'Request body is not a valid gzip';
err = new BadRequestError(msg);
} else {
msg = 'Failed to gunzip request body';
err = new InternalServerError(msg);
}
next(err);
return;
}

if (!req.body.length) {
next();
return;
Expand All @@ -155,6 +175,10 @@ function bodyReader(options) {
gz = zlib.createGunzip();
gz.on('data', bodyWriter.write);
gz.once('end', done);
gz.once('error', function onGzError(err) {
gzError = err;
done();
});
req.once('end', gz.end.bind(gz));
} else {
unsupportedContentEncoding = req.headers['content-encoding'];
Expand Down
73 changes: 73 additions & 0 deletions test/plugins/bodyReader.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

// core requires
var http = require('http');
var zlib = require('zlib');

// external requires
var assert = require('chai').assert;
Expand Down Expand Up @@ -65,6 +66,78 @@ describe('body reader', function() {
);
});

it('should return 400 if body is not a valid gzip', function(done) {
SERVER.use(restify.plugins.bodyParser());

CLIENT = restifyClients.createJsonClient({
url: 'http://127.0.0.1:' + PORT,
retry: false,
headers: {
'content-encoding': 'gzip'
}
});

SERVER.post('/compressed', function(req, res, next) {
res.send();
next();
});

CLIENT.post(
'/compressed',
{
apple: 'red'
},
function(err, _, res) {
assert.isOk(err, 'should fail');
assert.equal(res.statusCode, 400);
done();
}
);
});

describe('when maxBodySize given should', function() {
var body = { apple: 'red' };
var bodyTooLarge = { apple: 'red', lemon: 'yellow' };

var compressedBodySize = zlib.gzipSync(JSON.stringify(body))
.byteLength;

beforeEach(function() {
SERVER.use(
restify.plugins.bodyParser({
maxBodySize: compressedBodySize
})
);

CLIENT = restifyClients.createJsonClient({
url: 'http://127.0.0.1:' + PORT,
retry: false,
gzip: {}
});

SERVER.post('/compressed', function(req, res, next) {
res.send();
next();
});
});

it('return 200 when body size under the limit', function(done) {
CLIENT.post('/compressed', body, function(err, _, res) {
assert.ifError(err);
assert.equal(res.statusCode, 200);
done();
});
});

it('return 413 when body size over the limit', function(done) {
CLIENT.post('/compressed', bodyTooLarge, function(err, _, res) {
assert.isOk(err, 'should fail');
assert.equal(res.statusCode, 413);
done();
});
});
});

it('should not accept unsupported content encoding', function(done) {
SERVER.use(restify.plugins.bodyParser());

Expand Down