Skip to content

Commit

Permalink
fix: apply Content-Length header when missing
Browse files Browse the repository at this point in the history
* Certain requests (e.g. GET and DELETE) would fail when they included a body due to missing content-length header. See #147, #106, #130
  • Loading branch information
brettstack committed Jun 25, 2018
1 parent 086714c commit d39a047
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 14 deletions.
2 changes: 2 additions & 0 deletions __tests__/unit.js
Expand Up @@ -69,6 +69,7 @@ test('mapApiGatewayEventToHttpRequest: with headers', () => {
path: '/foo',
headers: {
'x-foo': 'foo',
'Content-Length': Buffer.byteLength('Hello serverless!'),
'x-apigateway-event': encodeURIComponent(JSON.stringify(r.eventClone)),
'x-apigateway-context': encodeURIComponent(JSON.stringify(r.context))
},
Expand All @@ -83,6 +84,7 @@ test('mapApiGatewayEventToHttpRequest: without headers', () => {
method: 'GET',
path: '/foo',
headers: {
'Content-Length': Buffer.byteLength('Hello serverless!'),
'x-apigateway-event': encodeURIComponent(JSON.stringify(r.eventClone)),
'x-apigateway-context': encodeURIComponent(JSON.stringify(r.context))
},
Expand Down
7 changes: 2 additions & 5 deletions package.json
Expand Up @@ -73,10 +73,7 @@
"examples/*/node_modules"
],
"jest": {
"collectCoverageFrom": [
"index.js",
"middleware.js"
]
"collectCoverageFrom": ["src/*"]
},
"devDependencies": {
"@commitlint/config-conventional": "^6.1.0",
Expand All @@ -101,7 +98,7 @@
},
"scripts": {
"test": "jest",
"test-watch": "jest --watch",
"test:watch": "jest --watch",
"coverage": "jest --coverage",
"cz": "git-cz",
"release": "semantic-release",
Expand Down
31 changes: 22 additions & 9 deletions src/index.js
Expand Up @@ -21,6 +21,13 @@ const isType = require('type-is')
function getPathWithQueryStringParams (event) {
return url.format({ pathname: event.path, query: event.queryStringParameters })
}
function getEventBody (event) {
return Buffer.from(event.body, event.isBase64Encoded ? 'base64' : 'utf8')
}

function clone (json) {
return JSON.parse(JSON.stringify(json))
}

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

function mapApiGatewayEventToHttpRequest (event, context, socketPath) {
const headers = event.headers || {} // NOTE: Mutating event.headers; prefer deep clone of event.headers
const eventWithoutBody = Object.assign({}, event)
delete eventWithoutBody.body
const headers = Object.assign({}, event.headers)

// NOTE: API Gateway is not setting Content-Length header on requests even when they have a body
if (event.body && !headers['Content-Length']) {
const body = getEventBody(event)
headers['Content-Length'] = Buffer.byteLength(body)
}

const clonedEventWithoutBody = clone(event)
delete clonedEventWithoutBody.body

headers['x-apigateway-event'] = encodeURIComponent(JSON.stringify(eventWithoutBody))
headers['x-apigateway-event'] = encodeURIComponent(JSON.stringify(clonedEventWithoutBody))
headers['x-apigateway-context'] = encodeURIComponent(JSON.stringify(context))

return {
Expand Down Expand Up @@ -119,13 +133,12 @@ function forwardLibraryErrorResponseToApiGateway (server, error, context) {
function forwardRequestToNodeServer (server, event, context) {
try {
const requestOptions = mapApiGatewayEventToHttpRequest(event, context, getSocketPath(server._socketPathSuffix))
const req = http.request(requestOptions, (response, body) => forwardResponseToApiGateway(server, response, context))
const req = http.request(requestOptions, (response) => forwardResponseToApiGateway(server, response, context))

if (event.body) {
if (event.isBase64Encoded) {
event.body = Buffer.from(event.body, 'base64')
}
const body = getEventBody(event)

req.write(event.body)
req.write(body)
}

req.on('error', (error) => forwardConnectionErrorResponseToApiGateway(server, error, context))
Expand Down

0 comments on commit d39a047

Please sign in to comment.