diff --git a/README.md b/README.md index 4d120b9..dfb21ec 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ $ npm i @fastify/aws-lambda | property | description | default value | | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ | ------------- | | binaryMimeTypes | Array of binary MimeTypes to handle | `[]` | +| enforceBase64 | Function that receives the reply and returns a boolean indicating if the response content is binary or not and should be base64-encoded | `undefined` | | serializeLambdaArguments | Activate the serialization of lambda Event and Context in http header `x-apigateway-event` `x-apigateway-context` | `false` *(was `true` for boolean; } export interface LambdaResponse { diff --git a/index.js b/index.js index 531b263..d54ad3c 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,13 @@ +const isCompressedDefault = (res) => { + const contentEncoding = res.headers['content-encoding'] || res.headers['Content-Encoding'] + return contentEncoding && contentEncoding !== 'identity' +} + +const customBinaryCheck = (options, res) => { + const enforceBase64 = typeof options.enforceBase64 === 'function' ? options.enforceBase64 : isCompressedDefault + return enforceBase64(res) === true +} + module.exports = (app, options) => { options = options || {} options.binaryMimeTypes = options.binaryMimeTypes || [] @@ -110,7 +120,7 @@ module.exports = (app, options) => { }) const contentType = (res.headers['content-type'] || res.headers['Content-Type'] || '').split(';')[0] - const isBase64Encoded = options.binaryMimeTypes.indexOf(contentType) > -1 + const isBase64Encoded = options.binaryMimeTypes.indexOf(contentType) > -1 || customBinaryCheck(options, res) const ret = { statusCode: res.statusCode, diff --git a/index.test-d.ts b/index.test-d.ts index f12376f..4926e6b 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -64,6 +64,14 @@ expectAssignable({ decorateRequest: true, decorationPropertyName: "myAWSstuff", }); +expectAssignable({ + binaryMimeTypes: ["foo", "bar"], + callbackWaitsForEmptyEventLoop: true, + serializeLambdaArguments: false, + decorateRequest: true, + decorationPropertyName: "myAWSstuff", + enforceBase64: (reply) => false, +}); expectError(awsLambdaFastify()); expectError(awsLambdaFastify(app, { neh: "definition" })); diff --git a/test/basic.test.js b/test/basic.test.js index 4a50d57..82d55e5 100644 --- a/test/basic.test.js +++ b/test/basic.test.js @@ -81,6 +81,86 @@ test('GET with base64 encoding response', async (t) => { t.equal(ret.headers['set-cookie'], 'qwerty=one') }) +test('GET with content-encoding response', async (t) => { + t.plan(16) + + const readFileAsync = promisify(fs.readFile) + const fileBuffer = await readFileAsync(__filename) + const app = fastify() + app.get('/test', async (request, reply) => { + t.equal(request.headers['x-my-header'], 'wuuusaaa') + t.equal(request.headers['x-apigateway-event'], '%7B%22httpMethod%22%3A%22GET%22%2C%22path%22%3A%22%2Ftest%22%2C%22headers%22%3A%7B%22X-My-Header%22%3A%22wuuusaaa%22%2C%22Content-Type%22%3A%22application%2Fjson%22%7D%7D') + t.equal(request.headers['user-agent'], 'lightMyRequest') + t.equal(request.headers.host, 'localhost:80') + t.equal(request.headers['content-length'], '0') + reply.header('Set-Cookie', 'qwerty=one') + reply.header('content-encoding', 'br') + reply.send(fileBuffer) + }) + const proxy = awsLambdaFastify(app, { binaryMimeTypes: [], serializeLambdaArguments: true }) + const ret = await proxy({ + httpMethod: 'GET', + path: '/test', + headers: { + 'X-My-Header': 'wuuusaaa', + 'Content-Type': 'application/json' + } + }) + t.equal(ret.statusCode, 200) + t.equal(ret.body, fileBuffer.toString('base64')) + t.equal(ret.isBase64Encoded, true) + t.ok(ret.headers) + t.equal(ret.headers['content-type'], 'application/octet-stream') + t.ok(ret.headers['content-length']) + t.ok(ret.headers.date) + t.equal(ret.headers.connection, 'keep-alive') + t.same(ret.multiValueHeaders, undefined) + t.equal(ret.headers['set-cookie'], 'qwerty=one') + t.equal(ret.headers['content-encoding'], 'br') +}) + +test('GET with custom binary check response', async (t) => { + t.plan(16) + + const readFileAsync = promisify(fs.readFile) + const fileBuffer = await readFileAsync(__filename) + const app = fastify() + app.get('/test', async (request, reply) => { + t.equal(request.headers['x-my-header'], 'wuuusaaa') + t.equal(request.headers['x-apigateway-event'], '%7B%22httpMethod%22%3A%22GET%22%2C%22path%22%3A%22%2Ftest%22%2C%22headers%22%3A%7B%22X-My-Header%22%3A%22wuuusaaa%22%2C%22Content-Type%22%3A%22application%2Fjson%22%7D%7D') + t.equal(request.headers['user-agent'], 'lightMyRequest') + t.equal(request.headers.host, 'localhost:80') + t.equal(request.headers['content-length'], '0') + reply.header('Set-Cookie', 'qwerty=one') + reply.header('X-Base64-Encoded', '1') + reply.send(fileBuffer) + }) + const proxy = awsLambdaFastify(app, { + binaryMimeTypes: [], + serializeLambdaArguments: true, + enforceBase64: (reply) => reply.headers['x-base64-encoded'] === '1' + }) + const ret = await proxy({ + httpMethod: 'GET', + path: '/test', + headers: { + 'X-My-Header': 'wuuusaaa', + 'Content-Type': 'application/json' + } + }) + t.equal(ret.statusCode, 200) + t.equal(ret.body, fileBuffer.toString('base64')) + t.equal(ret.isBase64Encoded, true) + t.ok(ret.headers) + t.equal(ret.headers['content-type'], 'application/octet-stream') + t.ok(ret.headers['content-length']) + t.ok(ret.headers.date) + t.equal(ret.headers.connection, 'keep-alive') + t.same(ret.multiValueHeaders, undefined) + t.equal(ret.headers['set-cookie'], 'qwerty=one') + t.equal(ret.headers['x-base64-encoded'], '1') +}) + test('GET with multi-value query params', async (t) => { t.plan(2)