Skip to content

Commit

Permalink
feat: lay groundwork for different event sources
Browse files Browse the repository at this point in the history
rename middleware from x-apigateway to x-lambda
  • Loading branch information
brettstack committed Apr 29, 2019
1 parent 149b14a commit 2db86f2
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 53 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ This package includes middleware to easily get the event object Lambda receives
const awsServerlessExpressMiddleware = require('aws-serverless-express/middleware')
app.use(awsServerlessExpressMiddleware.eventContext())
app.get('/', (req, res) => {
res.json(req.apiGateway.event)
res.json(req.lambda.event)
})
```

Expand Down
40 changes: 20 additions & 20 deletions __tests__/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ const mockNext = () => true
const generateMockReq = () => {
return {
headers: {
'x-apigateway-event': encodeURIComponent(JSON.stringify({
'x-lambda-event': encodeURIComponent(JSON.stringify({
path: '/foo/bar',
queryStringParameters: {
foo: '🖖',
bar: '~!@#$%^&*()_+`-=;\':",./<>?`'
}
})),
'x-apigateway-context': encodeURIComponent(JSON.stringify({foo: 'bar'}))
'x-lambda-context': encodeURIComponent(JSON.stringify({foo: 'bar'}))
}
}
}
Expand All @@ -24,10 +24,10 @@ test('defaults', () => {

eventContextMiddleware()(req, mockRes, mockNext)

expect(req.apiGateway.event).toEqual(JSON.parse(decodeURIComponent(originalHeaders['x-apigateway-event'])))
expect(req.apiGateway.context).toEqual(JSON.parse(decodeURIComponent(originalHeaders['x-apigateway-context'])))
expect(req.headers['x-apigateway-event']).toBe(undefined)
expect(req.headers['x-apigateway-context']).toBe(undefined)
expect(req.lambda.event).toEqual(JSON.parse(decodeURIComponent(originalHeaders['x-lambda-event'])))
expect(req.lambda.context).toEqual(JSON.parse(decodeURIComponent(originalHeaders['x-lambda-context'])))
expect(req.headers['x-lambda-event']).toBe(undefined)
expect(req.headers['x-lambda-context']).toBe(undefined)
})

test('options.reqPropKey', () => {
Expand All @@ -36,10 +36,10 @@ test('options.reqPropKey', () => {

eventContextMiddleware({ reqPropKey: '_apiGateway' })(req, mockRes, mockNext)

expect(req._apiGateway.event).toEqual(JSON.parse(decodeURIComponent(originalHeaders['x-apigateway-event'])))
expect(req._apiGateway.context).toEqual(JSON.parse(decodeURIComponent(originalHeaders['x-apigateway-context'])))
expect(req.headers['x-apigateway-event']).toBe(undefined)
expect(req.headers['x-apigateway-context']).toBe(undefined)
expect(req._apiGateway.event).toEqual(JSON.parse(decodeURIComponent(originalHeaders['x-lambda-event'])))
expect(req._apiGateway.context).toEqual(JSON.parse(decodeURIComponent(originalHeaders['x-lambda-context'])))
expect(req.headers['x-lambda-event']).toBe(undefined)
expect(req.headers['x-lambda-context']).toBe(undefined)
})

test('options.deleteHeaders = false', () => {
Expand All @@ -48,26 +48,26 @@ test('options.deleteHeaders = false', () => {

eventContextMiddleware({ deleteHeaders: false })(req, mockRes, mockNext)

expect(req.apiGateway.event).toEqual(JSON.parse(decodeURIComponent(originalHeaders['x-apigateway-event'])))
expect(req.apiGateway.context).toEqual(JSON.parse(decodeURIComponent(originalHeaders['x-apigateway-context'])))
expect(req.headers['x-apigateway-event']).toEqual(originalHeaders['x-apigateway-event'])
expect(req.headers['x-apigateway-context']).toEqual(originalHeaders['x-apigateway-context'])
expect(req.lambda.event).toEqual(JSON.parse(decodeURIComponent(originalHeaders['x-lambda-event'])))
expect(req.lambda.context).toEqual(JSON.parse(decodeURIComponent(originalHeaders['x-lambda-context'])))
expect(req.headers['x-lambda-event']).toEqual(originalHeaders['x-lambda-event'])
expect(req.headers['x-lambda-context']).toEqual(originalHeaders['x-lambda-context'])
})

test('Missing x-apigateway-event', () => {
test('Missing x-lambda-event', () => {
const req = generateMockReq()
delete req.headers['x-apigateway-event']
delete req.headers['x-lambda-event']

eventContextMiddleware({ deleteHeaders: false })(req, mockRes, mockNext)

expect(req.apiGateway).toBe(undefined)
expect(req.lambda).toBe(undefined)
})

test('Missing x-apigateway-context', () => {
test('Missing x-lambda-context', () => {
const req = generateMockReq()
delete req.headers['x-apigateway-context']
delete req.headers['x-lambda-context']

eventContextMiddleware({ deleteHeaders: false })(req, mockRes, mockNext)

expect(req.apiGateway).toBe(undefined)
expect(req.lambda).toBe(undefined)
})
8 changes: 4 additions & 4 deletions __tests__/unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ test('mapApiGatewayEventToHttpRequest: with headers', () => {
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))
'x-lambda-event': encodeURIComponent(JSON.stringify(r.eventClone)),
'x-lambda-context': encodeURIComponent(JSON.stringify(r.context))
},
socketPath: '/tmp/server0.sock'
})
Expand All @@ -87,8 +87,8 @@ test('mapApiGatewayEventToHttpRequest: without headers', () => {
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))
'x-lambda-event': encodeURIComponent(JSON.stringify(r.eventClone)),
'x-lambda-context': encodeURIComponent(JSON.stringify(r.context))
},
socketPath: '/tmp/server0.sock'
})
Expand Down
4 changes: 2 additions & 2 deletions examples/basic-starter/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const express = require('express')
const bodyParser = require('body-parser')
const cors = require('cors')
const compression = require('compression')
const awsServerlessExpressMiddleware = require('aws-serverless-express/middleware')
const awsServerlessExpressMiddleware = require(process.env.NODE_ENV === 'test' ? '../../middleware' : 'aws-serverless-express/middleware')
const app = express()
const router = express.Router()

Expand All @@ -28,7 +28,7 @@ app.set('views', path.join(__dirname, 'views'))

router.get('/', (req, res) => {
res.render('index', {
apiUrl: req.apiGateway ? `https://${req.apiGateway.event.headers.Host}/${req.apiGateway.event.requestContext.stage}` : 'http://localhost:3000'
apiUrl: req.lambda ? `https://${req.lambda.event.headers.Host}/${req.lambda.event.requestContext.stage}` : 'http://localhost:3000'
})
})

Expand Down
77 changes: 58 additions & 19 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,17 @@ function isContentTypeBinaryMimeType ({ contentType, binaryMimeTypes }) {
return binaryMimeTypes.length > 0 && !!isType.is(contentType, binaryMimeTypes)
}

function mapApiGatewayEventToHttpRequest ({ event, context, socketPath }) {
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)
}

function mapEventToHttpRequest ({
event,
context,
socketPath,
headers = Object.assign({}, event.headers)
}) {
const clonedEventWithoutBody = clone({ object: event })
delete clonedEventWithoutBody.body

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

return {
method: event.httpMethod,
Expand All @@ -69,6 +66,30 @@ function mapApiGatewayEventToHttpRequest ({ event, context, socketPath }) {
}
}

function mapApiGatewayEventToHttpRequest ({ event, context, socketPath }) {
const httpRequest = mapEventToHttpRequest({ event, context, socketPath })

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

return httpRequest
}

function mapAlbEventToHttpRequest ({ event, context, socketPath }) {
const httpRequest = mapEventToHttpRequest({ event, context, socketPath })

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

return httpRequest
}

function forwardResponseToApiGateway ({ server, response, resolver }) {
let buf = []

Expand Down Expand Up @@ -150,14 +171,25 @@ function forwardLibraryErrorResponseToApiGateway ({ error, resolver }) {
})
}

function getEventMapperBasedOnEventSource ({ eventSource }) {
switch (eventSource) {
case 'ALB':
return mapAlbEventToHttpRequest
default:
return mapApiGatewayEventToHttpRequest
}
}

function forwardRequestToNodeServer ({
server,
event,
context,
resolver
resolver,
eventSource,
eventMapper = getEventMapperBasedOnEventSource({ eventSource })
}) {
try {
const requestOptions = mapApiGatewayEventToHttpRequest({
const requestOptions = eventMapper({
event,
context,
socketPath: getSocketPath({ socketPathSuffix: server._socketPathSuffix })
Expand Down Expand Up @@ -220,8 +252,10 @@ function createServer ({
}

function getEventSourceBasedOnEvent ({
eventSource
event
}) {
if (event && event.requestContext && event.requestContext.elb) return 'ALB'

return 'API_GATEWAY'
}

Expand Down Expand Up @@ -252,15 +286,17 @@ function proxy ({
server,
event,
context,
resolver
resolver,
eventSource
})
} else {
startServer({ server })
.on('listening', () => forwardRequestToNodeServer({
server,
event,
context,
resolver
resolver,
eventSource
}))
}
})
Expand All @@ -287,7 +323,8 @@ function makeResolver ({
function configure ({
app: configureApp,
binaryMimeTypes: configureBinaryMimeTypes = [],
resolutionMode: configureResolutionMode = 'CONTEXT_SUCCEED'
resolutionMode: configureResolutionMode = 'CONTEXT_SUCCEED',
eventSource
} = {}) {
function _createServer ({
app = configureApp,
Expand All @@ -306,14 +343,16 @@ function configure ({
resolutionMode = configureResolutionMode,
event,
context,
callback
callback,
eventSource
} = {}) {
return proxy({
server,
event,
context,
resolutionMode,
callback
callback,
eventSource
})
}

Expand Down
14 changes: 7 additions & 7 deletions src/middleware.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
module.exports.eventContext = ({
reqPropKey = 'apiGateway',
reqPropKey = 'lambda',
deleteHeaders = true
} = {}) => function apiGatewayEventParser (req, res, next) {
if (!req.headers['x-apigateway-event'] || !req.headers['x-apigateway-context']) {
console.error('Missing x-apigateway-event or x-apigateway-context header(s)')
if (!req.headers['x-lambda-event'] || !req.headers['x-lambda-context']) {
console.error('Missing x-lambda-event or x-lambda-context header(s)')
next()
return
}

req[reqPropKey] = {
event: JSON.parse(decodeURIComponent(req.headers['x-apigateway-event'])),
context: JSON.parse(decodeURIComponent(req.headers['x-apigateway-context']))
event: JSON.parse(decodeURIComponent(req.headers['x-lambda-event'])),
context: JSON.parse(decodeURIComponent(req.headers['x-lambda-context']))
}

if (deleteHeaders) {
delete req.headers['x-apigateway-event']
delete req.headers['x-apigateway-context']
delete req.headers['x-lambda-event']
delete req.headers['x-lambda-context']
}

next()
Expand Down

0 comments on commit 2db86f2

Please sign in to comment.