Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ $ npm i @fastify/aws-lambda
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------- |
| binaryMimeTypes | Array of binary MimeTypes to handle | `[]` |
| enforceBase64 | Function that receives the response and returns a boolean indicating if the response content is binary or not and should be base64-encoded | `undefined` |
| disableBase64Encoding | Disable base64 encoding of responses and omit the `isBase64Encoded` property. When `undefined`, it is automatically enabled when `payloadAsStream` is `true` and the request does not come from an ALB. | `undefined` |
| serializeLambdaArguments | Activate the serialization of lambda Event and Context in http header `x-apigateway-event` `x-apigateway-context` | `false` *(was `true` for <v2.0.0)* |
| decorateRequest | Decorates the fastify request with the lambda Event and Context `request.awsLambda.event` `request.awsLambda.context` | `true` |
| decorationPropertyName | The default property name for request decoration | `awsLambda` |
Expand Down
23 changes: 19 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ const customBinaryCheck = (options, res) => {
return enforceBase64(res) === true
}

const disableBase64EncodingDefault = (options) => {
if (options.payloadAsStream) {
return (event) => !event.requestContext?.elb
}
return (_event) => false
}

module.exports = (app, options) => {
options = options || {}
options.binaryMimeTypes = options.binaryMimeTypes || []
Expand All @@ -20,6 +27,12 @@ module.exports = (app, options) => {
options.parseCommaSeparatedQueryParams = options.parseCommaSeparatedQueryParams !== undefined ? options.parseCommaSeparatedQueryParams : true
options.payloadAsStream = options.payloadAsStream !== undefined ? options.payloadAsStream : false
options.albMultiValueHeaders = options.albMultiValueHeaders !== undefined ? options.albMultiValueHeaders : false
if (options.disableBase64Encoding === undefined) {
options.disableBase64Encoding = disableBase64EncodingDefault(options)
} else {
const opt = options.disableBase64Encoding
options.disableBase64Encoding = (_event) => opt
}
let currentAwsArguments = {}
if (options.decorateRequest) {
options.decorationPropertyName = options.decorationPropertyName || 'awsLambda'
Expand Down Expand Up @@ -160,15 +173,17 @@ module.exports = (app, options) => {
}
})

const isBase64Disabled = options.disableBase64Encoding(event)
const contentType = (res.headers['content-type'] || res.headers['Content-Type'] || '').split(';', 1)[0]
const isBase64Encoded = options.binaryMimeTypes.indexOf(contentType) > -1 || customBinaryCheck(options, res)
const shouldBase64Encode = !isBase64Disabled && (options.binaryMimeTypes.indexOf(contentType) > -1 || customBinaryCheck(options, res))

const ret = {
statusCode: res.statusCode,
headers: res.headers,
isBase64Encoded
headers: res.headers
}

if (!isBase64Disabled) ret.isBase64Encoded = shouldBase64Encode

if (cookies && event.version === '2.0') ret.cookies = cookies
if (multiValueHeaders && (!event.version || event.version === '1.0')) ret.multiValueHeaders = multiValueHeaders
if (options.albMultiValueHeaders) {
Expand All @@ -179,7 +194,7 @@ module.exports = (app, options) => {
}

if (!options.payloadAsStream) {
ret.body = isBase64Encoded ? res.rawPayload.toString('base64') : res.payload
ret.body = shouldBase64Encode ? res.rawPayload.toString('base64') : res.payload
return resolve(ret)
}

Expand Down
29 changes: 29 additions & 0 deletions test/alb.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

const { describe, it } = require('node:test')
const assert = require('node:assert')
const { Readable } = require('node:stream')
const fastify = require('fastify')
const awsLambdaFastify = require('../index')
const { accumulate } = require('./utils')

describe('ALB Tests', async () => {
it('GET request', async () => {
Expand Down Expand Up @@ -57,6 +59,33 @@ describe('ALB Tests', async () => {
assert.deepStrictEqual(ret.cookies, ['qwerty=one', 'qwerty=two'])
})

it('GET streamed response with payloadAsStream', async () => {
const app = fastify()
app.get('/stream', async (_request, reply) => {
reply.header('content-type', 'application/json; charset=utf-8')
return reply.send(Readable.from(JSON.stringify({ hello: 'world' })))
})

const proxy = awsLambdaFastify(app, {
payloadAsStream: true
})

const { meta, stream } = await proxy({
requestContext: { elb: { targetGroupArn: 'xxx' } },
httpMethod: 'GET',
path: '/stream',
headers: {
'X-My-Header': 'wuuusaaa'
}
})

assert.equal(meta.statusCode, 200)
assert.equal(meta.isBase64Encoded, false)

const data = await accumulate(stream)
assert.equal(data.toString(), '{"hello":"world"}')
})

it('GET Broken', async () => {
const event = {
requestContext: { http: { method: 'GET' } },
Expand Down
33 changes: 33 additions & 0 deletions test/basic.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,39 @@ describe('Basic Tests', () => {
assert.equal(ret.headers['set-cookie'], 'qwerty=one')
})

it('GET with base64 encoding disabled', async () => {
const fileBuffer = await readFileAsync(__filename)
const app = fastify()

app.get('/test', async (_request, reply) => {
reply.send(fileBuffer)
})

const proxy = awsLambdaFastify(app, {
binaryMimeTypes: ['application/octet-stream'],
serializeLambdaArequestrguments: true,
disableBase64Encoding: true
})

const ret = await proxy({
httpMethod: 'GET',
path: '/test',
headers: {
'Content-Type': 'application/json'
}
})

assert.equal(ret.statusCode, 200)
assert.equal(ret.body, fileBuffer.toString())
assert.equal(ret.isBase64Encoded, undefined)
assert.ok(ret.headers)
assert.equal(ret.headers['content-type'], 'application/octet-stream')
assert.ok(ret.headers['content-length'])
assert.ok(ret.headers.date)
assert.equal(ret.headers.connection, 'keep-alive')
assert.equal(ret.multiValueHeaders, undefined)
})

it('GET with content-encoding response', async () => {
const app1 = fastify()
const fileBuffer = await readFileAsync(__filename)
Expand Down
43 changes: 29 additions & 14 deletions test/stream.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,10 @@

const { describe, it } = require('node:test')
const assert = require('node:assert')
const { promisify } = require('node:util')
const { Readable } = require('node:stream')
const fastify = require('fastify')
const awsLambdaFastify = require('../index')

const accumulate = promisify(function (stream, cb) {
const chunks = []
stream.on('error', cb)
stream.on('data', (chunk) => {
chunks.push(chunk)
})
stream.on('end', () => {
cb(null, Buffer.concat(chunks))
})
})
const { accumulate } = require('./utils')

describe('Basic Stream Tests', () => {
it('GET a normal response as stream', async () => {
Expand Down Expand Up @@ -70,7 +59,7 @@ describe('Basic Stream Tests', () => {
const data = await accumulate(stream)
assert.equal(data.toString(), '{"hello":"world"}', 'Response body should match')

assert.equal(meta.isBase64Encoded, false, 'isBase64Encoded should be false')
assert.equal(meta.isBase64Encoded, undefined, 'isBase64Encoded should not be set')
assert.ok(meta.headers, 'Headers should be defined')
assert.equal(
meta.headers['content-type'],
Expand Down Expand Up @@ -147,7 +136,7 @@ describe('Basic Stream Tests', () => {
const data = await accumulate(stream)
assert.equal(data.toString(), '{"hello":"world"}', 'Response body should match')

assert.equal(meta.isBase64Encoded, false, 'isBase64Encoded should be false')
assert.equal(meta.isBase64Encoded, undefined, 'isBase64Encoded should not be set')
assert.ok(meta.headers, 'Headers should be defined')
assert.equal(
meta.headers['content-type'],
Expand All @@ -171,4 +160,30 @@ describe('Basic Stream Tests', () => {
'Cookies should match'
)
})

it('GET a streamed response as stream with disableBase64Encoding false', async () => {
const app = fastify()

const evt = {
version: '2.0',
httpMethod: 'GET',
path: '/test',
headers: {
'X-My-Header': 'wuuusaaa'
},
cookies: ['foo=bar'],
queryStringParameters: ''
}

app.get('/test', async (_request, reply) => {
reply.header('content-type', 'application/json; charset=utf-8')
return reply.send(Readable.from(JSON.stringify({ hello: 'world' })))
})

const proxy = awsLambdaFastify(app, { payloadAsStream: true, disableBase64Encoding: false })

const { meta } = await proxy(evt)

assert.equal(meta.isBase64Encoded, false)
})
})
18 changes: 18 additions & 0 deletions test/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use strict'

const accumulate = function (stream) {
return new Promise((resolve, reject) => {
const chunks = []
stream.on('error', reject)
stream.on('data', (chunk) => {
chunks.push(chunk)
})
stream.on('end', () => {
resolve(Buffer.concat(chunks))
})
})
}

module.exports = {
accumulate
}
3 changes: 2 additions & 1 deletion types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ declare namespace awsLambdaFastify {
decorateRequest?: boolean;
decorationPropertyName?: string;
enforceBase64?: (response: LightMyRequestResponse) => boolean;
disableBase64Encoding?: boolean;
retainStage?: boolean;
/**
* usually set to 'proxy', if used
Expand All @@ -38,7 +39,7 @@ declare namespace awsLambdaFastify {
export interface LambdaResponseBase {
statusCode: number;
headers: Record<string, string>;
isBase64Encoded: boolean;
isBase64Encoded?: boolean;
cookies?: string[];
}

Expand Down
3 changes: 3 additions & 0 deletions types/index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ expectAssignable<LambdaFastifyOptions>({
},
retainStage: true,
});
expectAssignable<LambdaFastifyOptions>({
disableBase64Encoding: true,
});

expectError(awsLambdaFastify());
expectError(awsLambdaFastify(app, { neh: "definition" }));