diff --git a/__tests__/integration.js b/__tests__/integration.js index 2c112ce4..de33044d 100644 --- a/__tests__/integration.js +++ b/__tests__/integration.js @@ -6,7 +6,7 @@ const app = require('../examples/basic-starter/app') const server = awsServerlessExpress.createServer(app) const lambdaFunction = { - handler: (event, context, resolutionMode, callback) => awsServerlessExpress.proxy(server, event, context, resolutionMode, callback) + handler: (event, context, resolutionMode, callback, _server) => awsServerlessExpress.proxy(_server || server, event, context, resolutionMode, callback) } function clone (json) { @@ -96,6 +96,7 @@ describe('integration tests', () => { succeed }) }) + test('GET JSON collection', (done) => { const succeed = response => { delete response.headers.date @@ -199,6 +200,27 @@ describe('integration tests', () => { .promise.then(succeed) }) + test('GET JSON single (resolutionMode = PROMISE; new server)', (done) => { + const succeed = response => { + delete response.headers.date + expect(response).toEqual(makeResponse({ + 'body': '{"id":1,"name":"Joe"}', + 'headers': { + 'content-length': '21', + 'etag': 'W/"15-rRboW+j/yFKqYqV6yklp53+fANQ"' + } + })) + newServer.close() + done() + } + const newServer = awsServerlessExpress.createServer(app) + lambdaFunction.handler(makeEvent({ + path: '/users/1', + httpMethod: 'GET' + }), {}, 'PROMISE', null, newServer) + .promise.then(succeed) + }) + test('GET JSON single 404', (done) => { const succeed = response => { delete response.headers.date 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..7acd512a 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "path": "@semantic-release/git", "assets": [ "package.json", + "package-lock.json", "CHANGELOG.md", "dist/**/*.{js|css}" ], @@ -104,7 +105,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))