From b0927b8192680ae63c0ef205a46c4e7d22ed4ab4 Mon Sep 17 00:00:00 2001 From: Brett Andrews Date: Mon, 25 Jun 2018 08:05:34 -0700 Subject: [PATCH] fix: apply Content-Length header when missing * Certain requests (e.g. GET and DELETE) would fail when they included a body due to missing content-length header. See #147, #106, #130 --- __tests__/unit.js | 2 ++ package.json | 2 +- src/index.js | 28 ++++++++++++++++++++-------- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/__tests__/unit.js b/__tests__/unit.js index 6462d1df..55f94ade 100644 --- a/__tests__/unit.js +++ b/__tests__/unit.js @@ -72,6 +72,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)) }, @@ -86,6 +87,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)) }, diff --git a/package.json b/package.json index 5827ea25..33a8e3e0 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ }, "scripts": { "test": "jest", - "test-watch": "jest --watch", + "test:watch": "jest --watch", "coverage": "jest --coverage", "cz": "git-cz", "release": "semantic-release", diff --git a/src/index.js b/src/index.js index 98fc2169..3db57d31 100644 --- a/src/index.js +++ b/src/index.js @@ -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 @@ -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) - headers['x-apigateway-event'] = encodeURIComponent(JSON.stringify(eventWithoutBody)) + // 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(clonedEventWithoutBody)) headers['x-apigateway-context'] = encodeURIComponent(JSON.stringify(context)) return { @@ -121,11 +135,9 @@ function forwardRequestToNodeServer (server, event, context, resolver) { const requestOptions = mapApiGatewayEventToHttpRequest(event, context, getSocketPath(server._socketPathSuffix)) const req = http.request(requestOptions, (response) => forwardResponseToApiGateway(server, response, resolver)) 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(error, resolver))