From c4ecdedb1d08c0faa4120f0e88dcf44e1c29c134 Mon Sep 17 00:00:00 2001 From: Federico Valido Date: Fri, 17 Jun 2022 22:37:29 -0300 Subject: [PATCH] Removing "accept" header from cache/originRequest policy when AutoWebP is disabled. This will improve cloudfront's cache hit ratio, and stop sending unnecesary header to origin. --- .../lib/back-end/back-end-construct.ts | 104 ++++++++++++++---- .../common-resources-construct.ts | 4 + .../constructs/lib/serverless-image-stack.ts | 1 + .../__snapshots__/constructs.test.ts.snap | 40 +++++-- 4 files changed, 122 insertions(+), 27 deletions(-) diff --git a/source/constructs/lib/back-end/back-end-construct.ts b/source/constructs/lib/back-end/back-end-construct.ts index 88b9d8cc9..3d51d2d4a 100644 --- a/source/constructs/lib/back-end/back-end-construct.ts +++ b/source/constructs/lib/back-end/back-end-construct.ts @@ -5,12 +5,12 @@ import * as path from "path"; import { LambdaRestApiProps, RestApi } from "aws-cdk-lib/aws-apigateway"; import { AllowedMethods, - CacheHeaderBehavior, - CachePolicy, - CacheQueryStringBehavior, + CfnCachePolicy, + CfnOriginRequestPolicy, DistributionProps, + ICachePolicy, IOrigin, - OriginRequestPolicy, + IOriginRequestPolicy, OriginSslPolicy, PriceClass, ViewerProtocolPolicy, @@ -21,11 +21,12 @@ import { Runtime } from "aws-cdk-lib/aws-lambda"; import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs"; import { LogGroup, RetentionDays } from "aws-cdk-lib/aws-logs"; import { IBucket } from "aws-cdk-lib/aws-s3"; -import { ArnFormat, Aws, Duration, Lazy, Stack } from "aws-cdk-lib"; +import { ArnFormat, Aws, Duration, Lazy, Resource, Stack } from "aws-cdk-lib"; import { Construct } from "constructs"; import { CloudFrontToApiGatewayToLambda } from "@aws-solutions-constructs/aws-cloudfront-apigateway-lambda"; import { addCfnSuppressRules } from "../../utils/utils"; +import { Conditions } from "../common-resources/common-resources-construct"; import { SolutionConstructProps } from "../types"; export interface BackEndProps extends SolutionConstructProps { @@ -35,6 +36,7 @@ export interface BackEndProps extends SolutionConstructProps { readonly logsBucket: IBucket; readonly uuid: string; readonly cloudFrontPriceClass: string; + readonly conditions: Conditions; } export class BackEnd extends Construct { @@ -135,21 +137,9 @@ export class BackEnd extends Construct { }, ]); - const cachePolicy = new CachePolicy(this, "CachePolicy", { - cachePolicyName: `ServerlessImageHandler-${props.uuid}`, - defaultTtl: Duration.days(1), - minTtl: Duration.seconds(1), - maxTtl: Duration.days(365), - enableAcceptEncodingGzip: true, - headerBehavior: CacheHeaderBehavior.allowList("origin", "accept"), - queryStringBehavior: CacheQueryStringBehavior.allowList("signature"), - }); + const cachePolicy = new CustomBackEndCachePolicy(this, "CachePolicy", props); - const originRequestPolicy = new OriginRequestPolicy(this, "OriginRequestPolicy", { - originRequestPolicyName: `ServerlessImageHandler-${props.uuid}`, - headerBehavior: CacheHeaderBehavior.allowList("origin", "accept"), - queryStringBehavior: CacheQueryStringBehavior.allowList("signature"), - }); + const originRequestPolicy = new CustomBackEndOriginRequestPolicy(this, "OriginRequestPolicy", props); const apiGatewayRestApi = RestApi.fromRestApiId( this, @@ -215,3 +205,79 @@ export class BackEnd extends Construct { this.domainName = imageHandlerCloudFrontApiGatewayLambda.cloudFrontWebDistribution.distributionDomainName; } } + +class CustomBackEndCachePolicy extends Resource implements ICachePolicy { + public readonly cachePolicyId: string; + + constructor(scope: Construct, id: string, props: BackEndProps) { + super(scope, id, { + physicalName: `ServerlessImageHandler-${props.uuid}`, + }); + + const cachePolicy = new CfnCachePolicy(this, "Resource", { + cachePolicyConfig: { + name: `ServerlessImageHandler-${props.uuid}`, + defaultTtl: Duration.days(1).toSeconds(), + minTtl: Duration.seconds(1).toSeconds(), + maxTtl: Duration.days(365).toSeconds(), + parametersInCacheKeyAndForwardedToOrigin: { + enableAcceptEncodingGzip: true, + enableAcceptEncodingBrotli: false, + queryStringsConfig: { + queryStringBehavior: "whitelist", + queryStrings: ["signature"], + }, + headersConfig: { + headerBehavior: "whitelist", + }, + cookiesConfig: { + cookieBehavior: "none", + }, + }, + }, + }); + + // https://github.com/aws/aws-cdk/issues/8396#issuecomment-857690411 + cachePolicy.addOverride( + "Properties.CachePolicyConfig.ParametersInCacheKeyAndForwardedToOrigin.HeadersConfig.Headers", + { + "Fn::If": [props.conditions.enableAutoWebPCondition.logicalId, ["origin", "accept"], ["origin"]], + } + ); + + this.cachePolicyId = cachePolicy.ref; + } +} + +class CustomBackEndOriginRequestPolicy extends Resource implements IOriginRequestPolicy { + public readonly originRequestPolicyId: string; + + constructor(scope: Construct, id: string, props: BackEndProps) { + super(scope, id, { + physicalName: `ServerlessImageHandler-${props.uuid}`, + }); + + const originRequestPolicy = new CfnOriginRequestPolicy(this, "Resource", { + originRequestPolicyConfig: { + name: `ServerlessImageHandler-${props.uuid}`, + headersConfig: { + headerBehavior: "whitelist", + }, + queryStringsConfig: { + queryStringBehavior: "whitelist", + queryStrings: ["signature"], + }, + cookiesConfig: { + cookieBehavior: "none", + }, + }, + }); + + // https://github.com/aws/aws-cdk/issues/8396#issuecomment-857690411 + originRequestPolicy.addOverride("Properties.OriginRequestPolicyConfig.HeadersConfig.Headers", { + "Fn::If": [props.conditions.enableAutoWebPCondition.logicalId, ["origin", "accept"], ["origin"]], + }); + + this.originRequestPolicyId = originRequestPolicy.ref; + } +} diff --git a/source/constructs/lib/common-resources/common-resources-construct.ts b/source/constructs/lib/common-resources/common-resources-construct.ts index 988605850..322649248 100644 --- a/source/constructs/lib/common-resources/common-resources-construct.ts +++ b/source/constructs/lib/common-resources/common-resources-construct.ts @@ -21,6 +21,7 @@ export interface Conditions { readonly enableSignatureCondition: CfnCondition; readonly enableDefaultFallbackImageCondition: CfnCondition; readonly enableCorsCondition: CfnCondition; + readonly enableAutoWebPCondition: CfnCondition; } export interface AppRegistryApplicationProps { @@ -55,6 +56,9 @@ export class CommonResources extends Construct { enableCorsCondition: new CfnCondition(this, "EnableCorsCondition", { expression: Fn.conditionEquals(props.corsEnabled, "Yes"), }), + enableAutoWebPCondition: new CfnCondition(this, "EnableAutoWebPCondition", { + expression: Fn.conditionEquals(props.autoWebP, "Yes"), + }), }; this.secretsManagerPolicy = new Policy(this, "SecretsManagerPolicy", { diff --git a/source/constructs/lib/serverless-image-stack.ts b/source/constructs/lib/serverless-image-stack.ts index f92104947..d36ad81e7 100644 --- a/source/constructs/lib/serverless-image-stack.ts +++ b/source/constructs/lib/serverless-image-stack.ts @@ -177,6 +177,7 @@ export class ServerlessImageHandlerStack extends Stack { logsBucket: commonResources.logsBucket, uuid: commonResources.customResources.uuid, cloudFrontPriceClass: cloudFrontPriceClassParameter.valueAsString, + conditions: commonResources.conditions, ...solutionConstructProps, }); diff --git a/source/constructs/test/__snapshots__/constructs.test.ts.snap b/source/constructs/test/__snapshots__/constructs.test.ts.snap index 06a901a79..330ed2f66 100644 --- a/source/constructs/test/__snapshots__/constructs.test.ts.snap +++ b/source/constructs/test/__snapshots__/constructs.test.ts.snap @@ -11,6 +11,14 @@ exports[`Serverless Image Handler Stack Snapshot 1`] = ` "Yes", ], }, + "CommonResourcesEnableAutoWebPCondition68405A08": { + "Fn::Equals": [ + { + "Ref": "AutoWebPParameter", + }, + "Yes", + ], + }, "CommonResourcesEnableCorsConditionA0615348": { "Fn::Equals": [ { @@ -398,10 +406,18 @@ exports[`Serverless Image Handler Stack Snapshot 1`] = ` "EnableAcceptEncodingGzip": true, "HeadersConfig": { "HeaderBehavior": "whitelist", - "Headers": [ - "origin", - "accept", - ], + "Headers": { + "Fn::If": [ + "CommonResourcesEnableAutoWebPCondition68405A08", + [ + "origin", + "accept", + ], + [ + "origin", + ], + ], + }, }, "QueryStringsConfig": { "QueryStringBehavior": "whitelist", @@ -1254,10 +1270,18 @@ exports[`Serverless Image Handler Stack Snapshot 1`] = ` }, "HeadersConfig": { "HeaderBehavior": "whitelist", - "Headers": [ - "origin", - "accept", - ], + "Headers": { + "Fn::If": [ + "CommonResourcesEnableAutoWebPCondition68405A08", + [ + "origin", + "accept", + ], + [ + "origin", + ], + ], + }, }, "Name": { "Fn::Join": [