diff --git a/source/constructs/lib/back-end/back-end-construct.ts b/source/constructs/lib/back-end/back-end-construct.ts index fff24a925..d223a0db3 100644 --- a/source/constructs/lib/back-end/back-end-construct.ts +++ b/source/constructs/lib/back-end/back-end-construct.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { LambdaRestApiProps, RestApi } from '@aws-cdk/aws-apigateway'; -import { AllowedMethods, CachePolicy, DistributionProps, IOrigin, OriginRequestPolicy, OriginSslPolicy, PriceClass, ViewerProtocolPolicy } from '@aws-cdk/aws-cloudfront'; +import { AllowedMethods, CfnCachePolicy, CfnOriginRequestPolicy, DistributionProps, ICachePolicy, IOrigin, IOriginRequestPolicy, OriginSslPolicy, PriceClass, ViewerProtocolPolicy } from '@aws-cdk/aws-cloudfront'; import { HttpOrigin } from '@aws-cdk/aws-cloudfront-origins'; import { Policy, PolicyStatement, Role, ServicePrincipal } from '@aws-cdk/aws-iam'; import { Code, Function as LambdaFunction, Runtime } from '@aws-cdk/aws-lambda'; @@ -12,6 +12,7 @@ import { ArnFormat, Aws, Construct, Duration, Lazy, Stack } from '@aws-cdk/core' 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 { @@ -23,6 +24,7 @@ export interface BackEndProps extends SolutionConstructProps { readonly logsBucket: IBucket; readonly uuid: string; readonly cloudFrontPriceClass: string; + readonly conditions: Conditions; } export class BackEnd extends Construct { @@ -90,33 +92,77 @@ export class BackEnd extends Construct { addCfnSuppressRules(imageHandlerLogGroup, [{ id: 'W84', reason: 'CloudWatch log group is always encrypted by default.' }]); - 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: { - behavior: 'whitelist', - headers: ['origin', 'accept'] - }, - queryStringBehavior: { - behavior: 'whitelist', - queryStrings: ['signature'] + const cachePolicy = new CfnCachePolicy(this, 'CachePolicy', { + 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"] + ] + } + ); + + const cachePolicyAdapter = new class implements ICachePolicy { + public readonly cachePolicyId = cachePolicy.ref; + }(); + + const originRequestPolicy = new CfnOriginRequestPolicy(this, 'OriginRequestPolicy', { + originRequestPolicyConfig: { + name: `ServerlessImageHandler-${props.uuid}`, + headersConfig: { + headerBehavior: 'whitelist', + }, + queryStringsConfig: { + queryStringBehavior: 'whitelist', + queryStrings: ['signature'] + }, + cookiesConfig: { + cookieBehavior: 'none' + } } }); - const originRequestPolicy = new OriginRequestPolicy(this, 'OriginRequestPolicy', { - originRequestPolicyName: `ServerlessImageHandler-${props.uuid}`, - headerBehavior: { - behavior: 'whitelist', - headers: ['origin', 'accept'] - }, - queryStringBehavior: { - behavior: 'whitelist', - queryStrings: ['signature'] + // 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"] + ] } - }); + ); + + const originRequestPolicyAdapter = new class implements IOriginRequestPolicy { + public readonly originRequestPolicyId = originRequestPolicy.ref; + }(); const apiGatewayRestApi = RestApi.fromRestApiId(this, 'ApiGatewayRestApi', Lazy.string({ produce: () => imageHandlerCloudFrontApiGatewayLambda.apiGateway.restApiId })); @@ -131,8 +177,8 @@ export class BackEnd extends Construct { origin: origin, allowedMethods: AllowedMethods.ALLOW_GET_HEAD, viewerProtocolPolicy: ViewerProtocolPolicy.HTTPS_ONLY, - originRequestPolicy: originRequestPolicy, - cachePolicy: cachePolicy + originRequestPolicy: originRequestPolicyAdapter, + cachePolicy: cachePolicyAdapter }, priceClass: props.cloudFrontPriceClass as PriceClass, enableLogging: true, diff --git a/source/constructs/lib/common-resources/common-resources-construct.ts b/source/constructs/lib/common-resources/common-resources-construct.ts index fceb8e921..152016815 100644 --- a/source/constructs/lib/common-resources/common-resources-construct.ts +++ b/source/constructs/lib/common-resources/common-resources-construct.ts @@ -22,6 +22,7 @@ export interface Conditions { readonly enableSignatureCondition: CfnCondition; readonly enableDefaultFallbackImageCondition: CfnCondition; readonly enableCorsCondition: CfnCondition; + readonly enableAutoWebPCondition: CfnCondition; } /** @@ -48,6 +49,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') }) }; diff --git a/source/constructs/lib/serverless-image-stack.ts b/source/constructs/lib/serverless-image-stack.ts index a902866f9..0c480d8b3 100644 --- a/source/constructs/lib/serverless-image-stack.ts +++ b/source/constructs/lib/serverless-image-stack.ts @@ -166,6 +166,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 62131bdd5..5bcf4d1d8 100644 --- a/source/constructs/test/__snapshots__/constructs.test.ts.snap +++ b/source/constructs/test/__snapshots__/constructs.test.ts.snap @@ -11,6 +11,14 @@ Object { "Yes", ], }, + "CommonResourcesEnableAutoWebPCondition68405A08": Object { + "Fn::Equals": Array [ + Object { + "Ref": "AutoWebPParameter", + }, + "Yes", + ], + }, "CommonResourcesEnableCorsConditionA0615348": Object { "Fn::Equals": Array [ Object { @@ -321,7 +329,7 @@ Object { }, }, "Resources": Object { - "BackEndCachePolicy1DCE9B1B": Object { + "BackEndCachePolicy85AF2390": Object { "Properties": Object { "CachePolicyConfig": Object { "DefaultTTL": 86400, @@ -349,10 +357,18 @@ Object { "EnableAcceptEncodingGzip": true, "HeadersConfig": Object { "HeaderBehavior": "whitelist", - "Headers": Array [ - "origin", - "accept", - ], + "Headers": Object { + "Fn::If": Array [ + "CommonResourcesEnableAutoWebPCondition68405A08", + Array [ + "origin", + "accept", + ], + Array [ + "origin", + ], + ], + }, }, "QueryStringsConfig": Object { "QueryStringBehavior": "whitelist", @@ -433,11 +449,11 @@ Object { "HEAD", ], "CachePolicyId": Object { - "Ref": "BackEndCachePolicy1DCE9B1B", + "Ref": "BackEndCachePolicy85AF2390", }, "Compress": true, "OriginRequestPolicyId": Object { - "Ref": "BackEndOriginRequestPolicy771345D7", + "Ref": "BackEndOriginRequestPolicyAF28A12A", }, "TargetOriginId": "TestStackBackEndImageHandlerCloudFrontApiGatewayLambdaCloudFrontToApiGatewayCloudFrontDistributionOrigin1A053AEB7", "ViewerProtocolPolicy": "https-only", @@ -1204,7 +1220,7 @@ Object { "Type": "AWS::Logs::LogGroup", "UpdateReplacePolicy": "Retain", }, - "BackEndOriginRequestPolicy771345D7": Object { + "BackEndOriginRequestPolicyAF28A12A": Object { "Properties": Object { "OriginRequestPolicyConfig": Object { "CookiesConfig": Object { @@ -1212,10 +1228,18 @@ Object { }, "HeadersConfig": Object { "HeaderBehavior": "whitelist", - "Headers": Array [ - "origin", - "accept", - ], + "Headers": Object { + "Fn::If": Array [ + "CommonResourcesEnableAutoWebPCondition68405A08", + Array [ + "origin", + "accept", + ], + Array [ + "origin", + ], + ], + }, }, "Name": Object { "Fn::Join": Array [