`headers` and `multiValueHeaders` are not set properly when using `hono/aws-lambda` with ALB
What version of Hono are you using?
^4.3.2
What runtime/platform is your app running on?
Node, AWS Lambda
What steps can reproduce the bug?
When using hono/aws-lambdaadapter with a Lambda that has an ALB in front of it. It shouldn't use the multiValueHeaders as default, as this feature is not default when using ALB with Lambda target. Moreover, the set-cookie behavior in the adapter might result in bugs in some cases.
Example :
import { handle } from "hono/aws-lambda";
import { getCookie, setCookie } from "hono/cookie";
import { Hono } from "hono";
const app = new Hono();
app.get("/set-headers", (ctx) => {
setCookie(ctx, "foo", "bar", {
secure: process.env.NODE_ENV === "production",
domain: `.${process.env.COOKIE_DOMAINE}`,
httpOnly: true,
maxAge: 60 * 10,
sameSite: "Lax",
});
setCookie(ctx, "fizz", "buzz", {
secure: process.env.NODE_ENV === "production",
domain: `.${process.env.COOKIE_DOMAINE}`,
httpOnly: true,
maxAge: 60 * 10,
sameSite: "Lax",
});
return ctx.redirect("/get-headers");
});
app.get("/get-headers", (ctx) => {
const fooCookie = getCookie(ctx, "foo");
const fizzCookie = getCookie(ctx, "fizz");
return ctx.json({
fooCookie,
fizzCookie,
});
});
export const handler = handle(app);
And CDK minimal config :
import * as cdk from '@aws-cdk/core';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as lambda from '@aws-cdk/aws-lambda';
import * as lambdaNodejs from '@aws-cdk/aws-lambda-nodejs';
import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2';
export class CdkAlbLambdaStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Create VPC
const vpc = new ec2.Vpc(this, 'MyVpc', {
maxAzs: 2
});
// Create Lambda function
const lambdaFunction = new lambdaNodejs.NodejsFunction(this, 'MyLambda', {
entry: 'lambda/index.ts', // Entry file for your Lambda function
handler: 'handler', // Exported function name in your entry file
});
// Target Groupe
const targetGroup = new elbv2.ApplicationTargetGroup(
this,
`${name}-target-group`,
{
vpc: this.alb.vpc,
targetType: elbv2.TargetType.LAMBDA,
targets: [new elbv2Targets.LambdaTarget(lambdaFunction)],
}
);
// Create ALB
const alb = new elbv2.ApplicationLoadBalancer(this, 'MyAlb', {
vpc,
internetFacing: true
});
const albCert = new acm.Certificate(this, "alb-cert", {
domainName: `${hostedZoneName}`,
validation: acm.CertificateValidation.fromDns(hostedZone),
});
const listener = alb.addListener("alb-http-listener", {
port: 80,
open: true,
});
// Create ALB Listener
// Add Lambda as a target to ALB Listener
listener.addTargets('LambdaTarget', {
port: 80,
targets: [targetGroup],
});
}
}
As I mentioned before the multiValueHeaders is not enabled by default, to enable it we must add the following
...
targetGroup.setAttribute("lambda.multi_value_headers.enabled", "true");
...
But when this behavior is enabled, the ALB will ignore the headers value and only take multiHeadersValue except in Hono adapter not all the headers are propagated from headers to multiValueHeaders. Disabling the multiValueHeaders will result in set-cookie being passed as an array so the ALB will only take the last value
Here is the result payload of the example with multiValueHeaders enabled :
{
"result": {
"body": "",
"headers": {
"access-control-allow-origin": "*",
"content-type": "text/plain;charset=UTF-8",
"location": "/get-headers"
},
"statusCode": 302,
"isBase64Encoded": false,
"multiValueHeaders": {
"set-cookie": [
"foo=bar; Max-Age=600; Domain=.mydomaine.com; Path=/; HttpOnly; SameSite=Lax",
"fizz=buzz; Max-Age=600; Domain=.mydomaine.com; Path=/; HttpOnly; SameSite=Lax"
]
}
}
}
More info from AWS website : https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html#multi-value-headers
What is the expected behavior?
Two solution are possible :
- Soluion 1: When the
set-cookieis available, and it's an array, flatten it and put it directly in theevent.cookiesvalue as string - Solution 2: When
multiValueHeadersis present, pass to it all the values fromheadersas well
What do you see instead?
No response
Additional information
I would love to contribute and create a PR for the fix. I have managed to create a wrapper around handle function for internal testing purposes.
Hi @yiss
Now #2585 is merged. Is this problem still occurring with it?
@yusukebe I haven't seen this change. I'm going to test it and report back
Yes I can confirm that it's still happening. It seems that ALB straight out ignores the headers if multiValueHeaders is present in the response
@yiss
Thank you for confirming. Can you create a PR to fix it?
@yusukebe I will raise the PR soon. I was thinking of adding an albProcessor to seperate the ALB behavior from the API Gateway for the multiHeadersValue. Any thoughts? Or should I just put the the change in createResult and createRequest ?
@yiss Thank you.
I was thinking of adding an albProcessor to seperate the ALB behavior from the API Gateway for the multiHeadersValue. Any thoughts? Or should I just put the the change in createResult and createRequest ?
@watany-dev Do you have any advance? ^
This approach looks good.
adding an albProcessor to seperate the ALB behavior from the API Gateway for the multiHeadersValue.