diff --git a/runtime_tests/lambda/index.test.ts b/runtime_tests/lambda/index.test.ts index fdc2c0c1..418c92ab 100644 --- a/runtime_tests/lambda/index.test.ts +++ b/runtime_tests/lambda/index.test.ts @@ -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 = { @@ -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', diff --git a/src/adapter/aws-lambda/handler.ts b/src/adapter/aws-lambda/handler.ts index a61b1f93..c3cb275b 100644 --- a/src/adapter/aws-lambda/handler.ts +++ b/src/adapter/aws-lambda/handler.ts @@ -22,6 +22,7 @@ export interface APIGatewayProxyEventV2 { version: string routeKey: string headers: Record + multiValueHeaders?: undefined cookies?: string[] rawPath: string rawQueryString: string @@ -63,7 +64,8 @@ export interface APIGatewayProxyEvent { // When calling Lambda through an Application Load Balancer export interface ALBProxyEvent { httpMethod: string - headers: Record + headers?: Record + multiValueHeaders?: Record path: string body: string | null isBase64Encoded: boolean @@ -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)) + } } }