From 26ab7d62c986ca1abcbefe7228f118a631b8e6ed Mon Sep 17 00:00:00 2001 From: Andrew Barba Date: Tue, 6 Jul 2021 23:54:28 -0400 Subject: [PATCH] Change brotli compression to an opt-in feature due to its excessive CPU usage. --- README.md | 10 +++++++++- __tests__/responses.unit.js | 2 +- index.js | 2 +- lib/compression.js | 10 +++++++--- lib/response.js | 3 ++- 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 7399683..8ee69f1 100644 --- a/README.md +++ b/README.md @@ -1411,7 +1411,15 @@ const api = require('lambda-api')({ }); ``` -The response will automatically be compressed based on the `Accept-Encoding` header in the request. Supported compressions are Brotli, Gzip and Deflate - in that priority order. +The response will automatically be compressed based on the `Accept-Encoding` header in the request. Supported compressions are Gzip and Deflate, with opt-in support for Brotli: + +```javascript +const api = require('lambda-api')({ + compression: ['br', 'gzip'], +}); +``` + +> Note: Brotli compression is significantly slower than Gzip due to its CPU intensive algorithm. Please test extensively before enabling on a production environment. For full control over the response compression, instantiate the API with `isBase64` set to true, and a custom serializer that returns a compressed response as a base64 encoded string. Also, don't forget to set the correct `content-encoding` header: diff --git a/__tests__/responses.unit.js b/__tests__/responses.unit.js index aa5fba9..fe39540 100644 --- a/__tests__/responses.unit.js +++ b/__tests__/responses.unit.js @@ -29,7 +29,7 @@ const api4 = require('../index')({ // Init API with compression const api5 = require('../index')({ version: 'v1.0', - compression: true + compression: ['br', 'gzip', 'deflate'] }) let event = { diff --git a/index.js b/index.js index 784f1bc..1950731 100644 --- a/index.js +++ b/index.js @@ -45,7 +45,7 @@ class API { ? props.headers : {}; this._compression = - props && typeof props.compression === 'boolean' + props && (typeof props.compression === 'boolean' || Array.isArray(props.compression)) ? props.compression : false; diff --git a/lib/compression.js b/lib/compression.js index 2601466..40cd4eb 100644 --- a/lib/compression.js +++ b/lib/compression.js @@ -8,7 +8,10 @@ const zlib = require('zlib'); -exports.compress = (input, headers) => { +const defaultEnabledEcodings = ['gzip', 'deflate']; + +exports.compress = (input, headers, _enabledEncodings) => { + const enabledEncodings = new Set(_enabledEncodings || defaultEnabledEcodings); const acceptEncodingHeader = headers['accept-encoding'] || ''; const acceptableEncodings = new Set( acceptEncodingHeader @@ -20,6 +23,7 @@ exports.compress = (input, headers) => { // Handle Brotli compression (Only supported in Node v10 and later) if ( acceptableEncodings.has('br') && + enabledEncodings.has('br') && typeof zlib.brotliCompressSync === 'function' ) { return { @@ -29,7 +33,7 @@ exports.compress = (input, headers) => { } // Handle Gzip compression - if (acceptableEncodings.has('gzip')) { + if (acceptableEncodings.has('gzip') && enabledEncodings.has('gzip')) { return { data: zlib.gzipSync(input), contentEncoding: 'gzip', @@ -37,7 +41,7 @@ exports.compress = (input, headers) => { } // Handle deflate compression - if (acceptableEncodings.has('deflate')) { + if (acceptableEncodings.has('deflate') && enabledEncodings.has('deflate')) { return { data: zlib.deflateSync(input), contentEncoding: 'deflate', diff --git a/lib/response.js b/lib/response.js index 68f60a2..0acb18f 100644 --- a/lib/response.js +++ b/lib/response.js @@ -566,7 +566,8 @@ class RESPONSE { if (this._compression && this._response.body) { const { data, contentEncoding } = compression.compress( this._response.body, - this._request.headers + this._request.headers, + Array.isArray(this._compression) ? this._compression : null ); if (contentEncoding) { Object.assign(this._response, {