Skip to content

Commit 5432a1e

Browse files
authored
Base64-encode content if content-encoding indicates that content is compressed, add enforceBase64 option (#118)
1 parent f592b0f commit 5432a1e

File tree

5 files changed

+102
-2
lines changed

5 files changed

+102
-2
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ $ npm i @fastify/aws-lambda
2323
| property | description | default value |
2424
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ | ------------- |
2525
| binaryMimeTypes | Array of binary MimeTypes to handle | `[]` |
26+
| 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` |
2627
| 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)* |
2728
| decorateRequest | Decorates the fastify request with the lambda Event and Context `request.awsLambda.event` `request.awsLambda.context` | `true` |
2829
| decorationPropertyName | The default property name for request decoration | `awsLambda` |

index.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { Context } from "aws-lambda";
2-
import { FastifyInstance } from "fastify";
2+
import { FastifyInstance, FastifyReply } from "fastify";
33

44
export interface LambdaFastifyOptions {
55
binaryMimeTypes?: string[];
66
callbackWaitsForEmptyEventLoop?: boolean;
77
serializeLambdaArguments?: boolean;
88
decorateRequest?: boolean;
99
decorationPropertyName?: string;
10+
enforceBase64?: (reply: FastifyReply) => boolean;
1011
}
1112

1213
export interface LambdaResponse {

index.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
const isCompressedDefault = (res) => {
2+
const contentEncoding = res.headers['content-encoding'] || res.headers['Content-Encoding']
3+
return contentEncoding && contentEncoding !== 'identity'
4+
}
5+
6+
const customBinaryCheck = (options, res) => {
7+
const enforceBase64 = typeof options.enforceBase64 === 'function' ? options.enforceBase64 : isCompressedDefault
8+
return enforceBase64(res) === true
9+
}
10+
111
module.exports = (app, options) => {
212
options = options || {}
313
options.binaryMimeTypes = options.binaryMimeTypes || []
@@ -110,7 +120,7 @@ module.exports = (app, options) => {
110120
})
111121

112122
const contentType = (res.headers['content-type'] || res.headers['Content-Type'] || '').split(';')[0]
113-
const isBase64Encoded = options.binaryMimeTypes.indexOf(contentType) > -1
123+
const isBase64Encoded = options.binaryMimeTypes.indexOf(contentType) > -1 || customBinaryCheck(options, res)
114124

115125
const ret = {
116126
statusCode: res.statusCode,

index.test-d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ expectAssignable<LambdaFastifyOptions>({
6464
decorateRequest: true,
6565
decorationPropertyName: "myAWSstuff",
6666
});
67+
expectAssignable<LambdaFastifyOptions>({
68+
binaryMimeTypes: ["foo", "bar"],
69+
callbackWaitsForEmptyEventLoop: true,
70+
serializeLambdaArguments: false,
71+
decorateRequest: true,
72+
decorationPropertyName: "myAWSstuff",
73+
enforceBase64: (reply) => false,
74+
});
6775

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

test/basic.test.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,86 @@ test('GET with base64 encoding response', async (t) => {
8181
t.equal(ret.headers['set-cookie'], 'qwerty=one')
8282
})
8383

84+
test('GET with content-encoding response', async (t) => {
85+
t.plan(16)
86+
87+
const readFileAsync = promisify(fs.readFile)
88+
const fileBuffer = await readFileAsync(__filename)
89+
const app = fastify()
90+
app.get('/test', async (request, reply) => {
91+
t.equal(request.headers['x-my-header'], 'wuuusaaa')
92+
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')
93+
t.equal(request.headers['user-agent'], 'lightMyRequest')
94+
t.equal(request.headers.host, 'localhost:80')
95+
t.equal(request.headers['content-length'], '0')
96+
reply.header('Set-Cookie', 'qwerty=one')
97+
reply.header('content-encoding', 'br')
98+
reply.send(fileBuffer)
99+
})
100+
const proxy = awsLambdaFastify(app, { binaryMimeTypes: [], serializeLambdaArguments: true })
101+
const ret = await proxy({
102+
httpMethod: 'GET',
103+
path: '/test',
104+
headers: {
105+
'X-My-Header': 'wuuusaaa',
106+
'Content-Type': 'application/json'
107+
}
108+
})
109+
t.equal(ret.statusCode, 200)
110+
t.equal(ret.body, fileBuffer.toString('base64'))
111+
t.equal(ret.isBase64Encoded, true)
112+
t.ok(ret.headers)
113+
t.equal(ret.headers['content-type'], 'application/octet-stream')
114+
t.ok(ret.headers['content-length'])
115+
t.ok(ret.headers.date)
116+
t.equal(ret.headers.connection, 'keep-alive')
117+
t.same(ret.multiValueHeaders, undefined)
118+
t.equal(ret.headers['set-cookie'], 'qwerty=one')
119+
t.equal(ret.headers['content-encoding'], 'br')
120+
})
121+
122+
test('GET with custom binary check response', async (t) => {
123+
t.plan(16)
124+
125+
const readFileAsync = promisify(fs.readFile)
126+
const fileBuffer = await readFileAsync(__filename)
127+
const app = fastify()
128+
app.get('/test', async (request, reply) => {
129+
t.equal(request.headers['x-my-header'], 'wuuusaaa')
130+
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')
131+
t.equal(request.headers['user-agent'], 'lightMyRequest')
132+
t.equal(request.headers.host, 'localhost:80')
133+
t.equal(request.headers['content-length'], '0')
134+
reply.header('Set-Cookie', 'qwerty=one')
135+
reply.header('X-Base64-Encoded', '1')
136+
reply.send(fileBuffer)
137+
})
138+
const proxy = awsLambdaFastify(app, {
139+
binaryMimeTypes: [],
140+
serializeLambdaArguments: true,
141+
enforceBase64: (reply) => reply.headers['x-base64-encoded'] === '1'
142+
})
143+
const ret = await proxy({
144+
httpMethod: 'GET',
145+
path: '/test',
146+
headers: {
147+
'X-My-Header': 'wuuusaaa',
148+
'Content-Type': 'application/json'
149+
}
150+
})
151+
t.equal(ret.statusCode, 200)
152+
t.equal(ret.body, fileBuffer.toString('base64'))
153+
t.equal(ret.isBase64Encoded, true)
154+
t.ok(ret.headers)
155+
t.equal(ret.headers['content-type'], 'application/octet-stream')
156+
t.ok(ret.headers['content-length'])
157+
t.ok(ret.headers.date)
158+
t.equal(ret.headers.connection, 'keep-alive')
159+
t.same(ret.multiValueHeaders, undefined)
160+
t.equal(ret.headers['set-cookie'], 'qwerty=one')
161+
t.equal(ret.headers['x-base64-encoded'], '1')
162+
})
163+
84164
test('GET with multi-value query params', async (t) => {
85165
t.plan(2)
86166

0 commit comments

Comments
 (0)