Skip to content

Commit

Permalink
fix(adapter): handle multi value headers in AWS Lambda (#2494)
Browse files Browse the repository at this point in the history
* Headers can be undefined on ALB

* Delegate headers from multiValueHeaders

* Add test

* Assert header values

* format

* Make multiValueHeaders optional so it can be omitted

* Avoid Object.entries when undefined

* Write tests in runtime_tests
  • Loading branch information
exoego committed Apr 17, 2024
1 parent 932307e commit acb56b8
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 5 deletions.
101 changes: 101 additions & 0 deletions runtime_tests/lambda/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,13 @@ describe('AWS Lambda Adapter for Hono', () => {
return c.text('Valid Cookies')
})

app.post('/headers', (c) => {
if (c.req.header('foo')?.includes('bar')) {
return c.json({ message: 'ok' })
}
return c.json({ message: 'fail' }, 400)
})

const handler = handle(app)

const testApiGatewayRequestContext = {
Expand Down Expand Up @@ -491,6 +498,100 @@ describe('AWS Lambda Adapter for Hono', () => {
])
})

describe('headers', () => {
describe('single-value headers', () => {
it('Should extract single-value headers and return 200 (ALBProxyEvent)', async () => {
const event = {
body: '{}',
httpMethod: 'POST',
isBase64Encoded: false,
path: '/headers',
headers: {
host: 'localhost',
foo: 'bar',
},
requestContext: testALBRequestContext,
}
const apiGatewayResponseV2 = await handler(event)
expect(apiGatewayResponseV2.statusCode).toBe(200)
})

it('Should extract single-value headers and return 200 (APIGatewayProxyEvent)', async () => {
const apigatewayProxyEvent = {
version: '1.0',
resource: '/headers',
httpMethod: 'POST',
headers: {
host: 'localhost',
foo: 'bar',
},
path: '/headers',
body: null,
isBase64Encoded: false,
requestContext: testApiGatewayRequestContext,
}
const apiGatewayResponseV2 = await handler(apigatewayProxyEvent)
expect(apiGatewayResponseV2.statusCode).toBe(200)
})

it('Should extract single-value headers and return 200 (APIGatewayProxyEventV2)', async () => {
const apigatewayProxyV2Event = {
version: '2.0',
routeKey: '$default',
headers: {
host: 'localhost',
foo: 'bar',
},
rawPath: '/headers',
rawQueryString: '',
requestContext: testApiGatewayRequestContextV2,
resource: '/headers',
body: null,
isBase64Encoded: false,
}
const apiGatewayResponseV2 = await handler(apigatewayProxyV2Event)
expect(apiGatewayResponseV2.statusCode).toBe(200)
})
})

describe('multi-value headers', () => {
it('Should extract multi-value headers and return 200 (ALBProxyEvent)', async () => {
const event = {
body: '{}',
httpMethod: 'POST',
isBase64Encoded: false,
path: '/headers',
multiValueHeaders: {
host: ['localhost'],
foo: ['bar'],
},
requestContext: testALBRequestContext,
}
const apiGatewayResponseV2 = await handler(event)
expect(apiGatewayResponseV2.statusCode).toBe(200)
})

it('Should extract multi-value headers and return 200 (APIGatewayProxyEvent)', async () => {
const apigatewayProxyEvent = {
version: '1.0',
resource: '/headers',
httpMethod: 'POST',
headers: {},
multiValueHeaders: {
host: ['localhost'],
foo: ['bar'],
},
path: '/headers',
body: null,
isBase64Encoded: false,
requestContext: testApiGatewayRequestContext,
}
const apiGatewayResponseV2 = await handler(apigatewayProxyEvent)
expect(apiGatewayResponseV2.statusCode).toBe(200)
})
})
})

it('Should handle a POST request and return a 200 response if cookies match (APIGatewayProxyEvent V1 and V2)', async () => {
const apiGatewayEvent = {
version: '1.0',
Expand Down
21 changes: 16 additions & 5 deletions src/adapter/aws-lambda/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export interface APIGatewayProxyEventV2 {
version: string
routeKey: string
headers: Record<string, string | undefined>
multiValueHeaders?: undefined
cookies?: string[]
rawPath: string
rawQueryString: string
Expand Down Expand Up @@ -63,7 +64,8 @@ export interface APIGatewayProxyEvent {
// When calling Lambda through an Application Load Balancer
export interface ALBProxyEvent {
httpMethod: string
headers: Record<string, string | undefined>
headers?: Record<string, string | undefined>
multiValueHeaders?: Record<string, string[] | undefined>
path: string
body: string | null
isBase64Encoded: boolean
Expand Down Expand Up @@ -198,16 +200,25 @@ const createRequest = (event: LambdaEvent) => {
const domainName =
event.requestContext && 'domainName' in event.requestContext
? event.requestContext.domainName
: event.headers['host']
: event.headers?.['host'] ?? event.multiValueHeaders?.['host']?.[0]
const path = isProxyEventV2(event) ? event.rawPath : event.path
const urlPath = `https://${domainName}${path}`
const url = queryString ? `${urlPath}?${queryString}` : urlPath

const headers = new Headers()
getCookies(event, headers)
for (const [k, v] of Object.entries(event.headers)) {
if (v) {
headers.set(k, v)
if (event.headers) {
for (const [k, v] of Object.entries(event.headers)) {
if (v) {
headers.set(k, v)
}
}
}
if (event.multiValueHeaders) {
for (const [k, values] of Object.entries(event.multiValueHeaders)) {
if (values) {
values.forEach((v) => headers.append(k, v))
}
}
}

Expand Down

0 comments on commit acb56b8

Please sign in to comment.