Skip to content

Commit de2ec33

Browse files
authored
Merge branch 'master' into refactor/get-path-with-query
2 parents f3ab08f + 28d9e35 commit de2ec33

7 files changed

Lines changed: 59 additions & 29 deletions

File tree

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,5 +80,4 @@ app.use(awsServerlessExpressMiddleware.eventContext())
8080
- May be more expensive for high-traffic apps
8181
- Cannot use native libraries (aka [Addons](https://nodejs.org/api/addons.html)) unless you package your app on an EC2 machine running Amazon Linux
8282
- Stateless only
83-
- Multiple headers with same name not currently supported
8483
- API Gateway has a timeout of 30 seconds, and Lambda has a maximum execution time of 5 minutes.

__tests__/index.js

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -95,34 +95,55 @@ test('getSocketPath', () => {
9595
expect(socketPath).toEqual('/tmp/server0.sock')
9696
})
9797

98-
describe('forwardResponseToApiGateway: content-type encoding', () => {
99-
const PassThrough = require('stream').PassThrough
100-
101-
class MockResponse extends PassThrough {
102-
constructor(statusCode, headers, body) {
103-
super()
104-
this.statusCode = statusCode
105-
this.headers = headers || {}
106-
this.write(body)
107-
this.end()
108-
}
98+
const PassThrough = require('stream').PassThrough
99+
100+
class MockResponse extends PassThrough {
101+
constructor(statusCode, headers, body) {
102+
super()
103+
this.statusCode = statusCode
104+
this.headers = headers || {}
105+
this.write(body)
106+
this.end()
109107
}
108+
}
110109

111-
class MockServer {
112-
constructor(binaryTypes) {
113-
this._binaryTypes = binaryTypes || []
114-
}
110+
class MockServer {
111+
constructor(binaryTypes) {
112+
this._binaryTypes = binaryTypes || []
115113
}
114+
}
116115

117-
class MockContext {
118-
constructor(resolve) {
119-
this.resolve = resolve
120-
}
121-
succeed(successResponse) {
122-
this.resolve(successResponse)
123-
}
116+
class MockContext {
117+
constructor(resolve) {
118+
this.resolve = resolve
119+
}
120+
succeed(successResponse) {
121+
this.resolve(successResponse)
124122
}
123+
}
125124

125+
describe('forwardResponseToApiGateway: header handling', () => {
126+
test('multiple headers with the same name get transformed', () => {
127+
const server = new MockServer()
128+
const headers = {'foo': ['bar', 'baz']}
129+
const body = 'hello world'
130+
const response = new MockResponse(200, headers, body)
131+
return new Promise(
132+
(resolve, reject) => {
133+
const context = new MockContext(resolve)
134+
awsServerlessExpress.forwardResponseToApiGateway(
135+
server, response, context)
136+
}
137+
).then(successResponse => expect(successResponse).toEqual({
138+
statusCode: 200,
139+
body: body,
140+
headers: { Foo: 'bar', fOo: 'baz' },
141+
isBase64Encoded: false
142+
}))
143+
})
144+
})
145+
146+
describe('forwardResponseToApiGateway: content-type encoding', () => {
126147
test('content-type header missing', () => {
127148
const server = new MockServer()
128149
const headers = {'foo': 'bar'}

example/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "aws-serverless-express-example",
3-
"version": "1.1.0",
3+
"version": "2.1.0",
44
"description": "Example application for running a Node Express app on AWS Lambda using Amazon API Gateway.",
55
"main": "lambda.js",
66
"config": {
@@ -22,7 +22,6 @@
2222
"package-deploy": "npm run package && npm run deploy",
2323
"delete-stack": "aws cloudformation delete-stack --stack-name $npm_package_config_cloudFormationStackName --region $npm_package_config_region",
2424
"setup": "npm install && (aws s3api get-bucket-location --bucket $npm_package_config_s3BucketName --region $npm_package_config_region || npm run create-bucket) && npm run package-deploy",
25-
2625
"win-config": "npm run config",
2726
"win-deconfig": "npm run deconfig",
2827
"win-local": "npm run local",

example/scripts/configure.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const accountId = args['account-id']
3333
const bucketName = args['bucket-name']
3434
const functionName = args['function-name']
3535
const region = args.region
36-
const availableRegions = ['us-east-1', 'us-west-2', 'eu-west-1', 'eu-central-1', 'ap-northeast-1', 'ap-northeast-2', 'ap-southeast-1', 'ap-southeast-2']
36+
const availableRegions = ['us-east-1', 'us-east-2', 'us-west-1', 'us-west-2', 'eu-west-1', 'eu-west-2', 'eu-central-1', 'ap-northeast-1', 'ap-northeast-2', 'ap-southeast-1', 'ap-southeast-2']
3737

3838
if (!accountId || accountId.length !== 12) {
3939
console.error('You must supply a 12 digit account id as --account-id="<accountId>"')

example/simple-proxy-api.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ paths:
1919
responses:
2020
default:
2121
statusCode: 200
22-
uri: arn:aws:apigateway:YOUR_AWS_REGION:lambda:path/2015-03-31/functions/arn:aws:lambda:YOUR_AWS_REGION:YOUR_ACCOUNT_ID:function:${stageVariables.YOUR_SERVERLESS_EXPRESS_LAMBDA_FUNCTION_NAME}/invocations
22+
uri: arn:aws:apigateway:YOUR_AWS_REGION:lambda:path/2015-03-31/functions/arn:aws:lambda:YOUR_AWS_REGION:YOUR_ACCOUNT_ID:function:${stageVariables.ServerlessExpressLambdaFunctionName}/invocations
2323
passthroughBehavior: when_no_match
2424
httpMethod: POST
2525
type: aws_proxy
@@ -63,7 +63,7 @@ paths:
6363
type: string
6464
responses: {}
6565
x-amazon-apigateway-integration:
66-
uri: arn:aws:apigateway:YOUR_AWS_REGION:lambda:path/2015-03-31/functions/arn:aws:lambda:YOUR_AWS_REGION:YOUR_ACCOUNT_ID:function:${stageVariables.YOUR_SERVERLESS_EXPRESS_LAMBDA_FUNCTION_NAME}/invocations
66+
uri: arn:aws:apigateway:YOUR_AWS_REGION:lambda:path/2015-03-31/functions/arn:aws:lambda:YOUR_AWS_REGION:YOUR_ACCOUNT_ID:function:${stageVariables.ServerlessExpressLambdaFunctionName}/invocations
6767
httpMethod: POST
6868
type: aws_proxy
6969
options:

index.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
'use strict'
1616
const http = require('http')
1717
const url = require('url')
18+
const binarycase = require('binary-case')
1819

1920
function getPathWithQueryStringParams(event) {
2021
return url.format({ pathname: event.path, query: event.queryStringParameters })
@@ -50,9 +51,16 @@ function forwardResponseToApiGateway(server, response, context) {
5051
const statusCode = response.statusCode
5152
const headers = response.headers
5253

54+
// HACK: modifies header casing to get around API Gateway's limitation of not allowing multiple
55+
// headers with the same name, as discussed on the AWS Forum https://forums.aws.amazon.com/message.jspa?messageID=725953#725953
5356
Object.keys(headers)
5457
.forEach(h => {
55-
if(Array.isArray(headers[h])) headers[h] = headers[h].join(',')
58+
if(Array.isArray(headers[h])) {
59+
headers[h].forEach((value, i) => {
60+
headers[binarycase(h, i + 1)] = value
61+
})
62+
delete headers[h]
63+
}
5664
})
5765

5866
const contentType = headers['content-type']

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,8 @@
3232
},
3333
"scripts": {
3434
"test": "jest"
35+
},
36+
"dependencies": {
37+
"binary-case": "^1.0.0"
3538
}
3639
}

0 commit comments

Comments
 (0)