Skip to content

Commit c2f416b

Browse files
authored
fix: apply Content-Length header when missing (#175)
2 parents bc7bdaf + 6eb8ad9 commit c2f416b

File tree

4 files changed

+47
-10
lines changed

4 files changed

+47
-10
lines changed

__tests__/integration.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const app = require('../examples/basic-starter/app')
66

77
const server = awsServerlessExpress.createServer(app)
88
const lambdaFunction = {
9-
handler: (event, context, resolutionMode, callback) => awsServerlessExpress.proxy(server, event, context, resolutionMode, callback)
9+
handler: (event, context, resolutionMode, callback, _server) => awsServerlessExpress.proxy(_server || server, event, context, resolutionMode, callback)
1010
}
1111

1212
function clone (json) {
@@ -96,6 +96,7 @@ describe('integration tests', () => {
9696
succeed
9797
})
9898
})
99+
99100
test('GET JSON collection', (done) => {
100101
const succeed = response => {
101102
delete response.headers.date
@@ -199,6 +200,27 @@ describe('integration tests', () => {
199200
.promise.then(succeed)
200201
})
201202

203+
test('GET JSON single (resolutionMode = PROMISE; new server)', (done) => {
204+
const succeed = response => {
205+
delete response.headers.date
206+
expect(response).toEqual(makeResponse({
207+
'body': '{"id":1,"name":"Joe"}',
208+
'headers': {
209+
'content-length': '21',
210+
'etag': 'W/"15-rRboW+j/yFKqYqV6yklp53+fANQ"'
211+
}
212+
}))
213+
newServer.close()
214+
done()
215+
}
216+
const newServer = awsServerlessExpress.createServer(app)
217+
lambdaFunction.handler(makeEvent({
218+
path: '/users/1',
219+
httpMethod: 'GET'
220+
}), {}, 'PROMISE', null, newServer)
221+
.promise.then(succeed)
222+
})
223+
202224
test('GET JSON single 404', (done) => {
203225
const succeed = response => {
204226
delete response.headers.date

__tests__/unit.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ test('mapApiGatewayEventToHttpRequest: with headers', () => {
7272
path: '/foo',
7373
headers: {
7474
'x-foo': 'foo',
75+
'Content-Length': Buffer.byteLength('Hello serverless!'),
7576
'x-apigateway-event': encodeURIComponent(JSON.stringify(r.eventClone)),
7677
'x-apigateway-context': encodeURIComponent(JSON.stringify(r.context))
7778
},
@@ -86,6 +87,7 @@ test('mapApiGatewayEventToHttpRequest: without headers', () => {
8687
method: 'GET',
8788
path: '/foo',
8889
headers: {
90+
'Content-Length': Buffer.byteLength('Hello serverless!'),
8991
'x-apigateway-event': encodeURIComponent(JSON.stringify(r.eventClone)),
9092
'x-apigateway-context': encodeURIComponent(JSON.stringify(r.context))
9193
},

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"path": "@semantic-release/git",
4747
"assets": [
4848
"package.json",
49+
"package-lock.json",
4950
"CHANGELOG.md",
5051
"dist/**/*.{js|css}"
5152
],
@@ -104,7 +105,7 @@
104105
},
105106
"scripts": {
106107
"test": "jest",
107-
"test-watch": "jest --watch",
108+
"test:watch": "jest --watch",
108109
"coverage": "jest --coverage",
109110
"cz": "git-cz",
110111
"release": "semantic-release",

src/index.js

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ const isType = require('type-is')
2121
function getPathWithQueryStringParams (event) {
2222
return url.format({ pathname: event.path, query: event.queryStringParameters })
2323
}
24+
function getEventBody (event) {
25+
return Buffer.from(event.body, event.isBase64Encoded ? 'base64' : 'utf8')
26+
}
27+
28+
function clone (json) {
29+
return JSON.parse(JSON.stringify(json))
30+
}
2431

2532
function getContentType (params) {
2633
// only compare mime type; ignore encoding part
@@ -32,11 +39,18 @@ function isContentTypeBinaryMimeType (params) {
3239
}
3340

3441
function mapApiGatewayEventToHttpRequest (event, context, socketPath) {
35-
const headers = event.headers || {} // NOTE: Mutating event.headers; prefer deep clone of event.headers
36-
const eventWithoutBody = Object.assign({}, event)
37-
delete eventWithoutBody.body
42+
const headers = Object.assign({}, event.headers)
3843

39-
headers['x-apigateway-event'] = encodeURIComponent(JSON.stringify(eventWithoutBody))
44+
// NOTE: API Gateway is not setting Content-Length header on requests even when they have a body
45+
if (event.body && !headers['Content-Length']) {
46+
const body = getEventBody(event)
47+
headers['Content-Length'] = Buffer.byteLength(body)
48+
}
49+
50+
const clonedEventWithoutBody = clone(event)
51+
delete clonedEventWithoutBody.body
52+
53+
headers['x-apigateway-event'] = encodeURIComponent(JSON.stringify(clonedEventWithoutBody))
4054
headers['x-apigateway-context'] = encodeURIComponent(JSON.stringify(context))
4155

4256
return {
@@ -121,11 +135,9 @@ function forwardRequestToNodeServer (server, event, context, resolver) {
121135
const requestOptions = mapApiGatewayEventToHttpRequest(event, context, getSocketPath(server._socketPathSuffix))
122136
const req = http.request(requestOptions, (response) => forwardResponseToApiGateway(server, response, resolver))
123137
if (event.body) {
124-
if (event.isBase64Encoded) {
125-
event.body = Buffer.from(event.body, 'base64')
126-
}
138+
const body = getEventBody(event)
127139

128-
req.write(event.body)
140+
req.write(body)
129141
}
130142

131143
req.on('error', (error) => forwardConnectionErrorResponseToApiGateway(error, resolver))

0 commit comments

Comments
 (0)