Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cloudfront): REST API origin #20335

Merged
merged 4 commits into from
May 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions packages/@aws-cdk/aws-cloudfront-origins/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,17 @@ new cloudfront.Distribution(this, 'myDist', {
},
});
```

## From an API Gateway REST API

Origins can be created from an API Gateway REST API. It is recommended to use a
[regional API](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-api-endpoint-types.html) in this case.

```ts
declare const api: apigateway.RestApi;
new cloudfront.Distribution(this, 'Distribution', {
defaultBehavior: { origin: new origins.RestApiOrigin(api) },
});
```

The origin path will automatically be set as the stage name.
12 changes: 1 addition & 11 deletions packages/@aws-cdk/aws-cloudfront-origins/lib/http-origin.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as cloudfront from '@aws-cdk/aws-cloudfront';
import * as cdk from '@aws-cdk/core';
import { validateSecondsInRangeOrUndefined } from './private/utils';

/**
* Properties for an Origin backed by an S3 website-configured bucket, load balancer, or custom HTTP server.
Expand Down Expand Up @@ -79,14 +80,3 @@ export class HttpOrigin extends cloudfront.OriginBase {
};
}
}

/**
* Throws an error if a duration is defined and not an integer number of seconds within a range.
*/
function validateSecondsInRangeOrUndefined(name: string, min: number, max: number, duration?: cdk.Duration) {
if (duration === undefined) { return; }
const value = duration.toSeconds();
if (!Number.isInteger(value) || value < min || value > max) {
throw new Error(`${name}: Must be an int between ${min} and ${max} seconds (inclusive); received ${value}.`);
}
}
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-cloudfront-origins/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './http-origin';
export * from './load-balancer-origin';
export * from './s3-origin';
export * from './origin-group';
export * from './rest-api-origin';
12 changes: 12 additions & 0 deletions packages/@aws-cdk/aws-cloudfront-origins/lib/private/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import * as cdk from '@aws-cdk/core';

/**
* Throws an error if a duration is defined and not an integer number of seconds within a range.
*/
export function validateSecondsInRangeOrUndefined(name: string, min: number, max: number, duration?: cdk.Duration) {
if (duration === undefined) { return; }
const value = duration.toSeconds();
if (!Number.isInteger(value) || value < min || value > max) {
throw new Error(`${name}: Must be an int between ${min} and ${max} seconds (inclusive); received ${value}.`);
}
}
59 changes: 59 additions & 0 deletions packages/@aws-cdk/aws-cloudfront-origins/lib/rest-api-origin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import * as apigateway from '@aws-cdk/aws-apigateway';
import * as cloudfront from '@aws-cdk/aws-cloudfront';
import * as cdk from '@aws-cdk/core';
import { validateSecondsInRangeOrUndefined } from './private/utils';

/**
* Properties for an Origin for an API Gateway REST API.
*/
export interface RestApiOriginProps extends cloudfront.OriginOptions {
/**
* Specifies how long, in seconds, CloudFront waits for a response from the origin, also known as the origin response timeout.
* The valid range is from 1 to 180 seconds, inclusive.
*
* Note that values over 60 seconds are possible only after a limit increase request for the origin response timeout quota
* has been approved in the target account; otherwise, values over 60 seconds will produce an error at deploy time.
*
* @default Duration.seconds(30)
*/
readonly readTimeout?: cdk.Duration;

/**
* Specifies how long, in seconds, CloudFront persists its connection to the origin.
* The valid range is from 1 to 180 seconds, inclusive.
*
* Note that values over 60 seconds are possible only after a limit increase request for the origin response timeout quota
* has been approved in the target account; otherwise, values over 60 seconds will produce an error at deploy time.
*
* @default Duration.seconds(5)
*/
readonly keepaliveTimeout?: cdk.Duration;
}

/**
* An Origin for an API Gateway REST API.
*/
export class RestApiOrigin extends cloudfront.OriginBase {

constructor(restApi: apigateway.RestApi, private readonly props: RestApiOriginProps = {}) {
// urlForPath() is of the form 'https://<rest-api-id>.execute-api.<region>.amazonaws.com/<stage>'
// Splitting on '/' gives: ['https', '', '<rest-api-id>.execute-api.<region>.amazonaws.com', '<stage>']
// The element at index 2 is the domain name, the element at index 3 is the stage name
super(cdk.Fn.select(2, cdk.Fn.split('/', restApi.url)), {
originPath: `/${cdk.Fn.select(3, cdk.Fn.split('/', restApi.url))}`,
...props,
});
Comment on lines +42 to +45

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't Fn.parseDomainName be used here instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't know about Fn.parseDomainName... but this version extracts the domain name with a single select and split...


validateSecondsInRangeOrUndefined('readTimeout', 1, 180, props.readTimeout);
validateSecondsInRangeOrUndefined('keepaliveTimeout', 1, 180, props.keepaliveTimeout);
}

protected renderCustomOriginConfig(): cloudfront.CfnDistribution.CustomOriginConfigProperty | undefined {
return {
originSslProtocols: [cloudfront.OriginSslPolicy.TLS_V1_2],
originProtocolPolicy: cloudfront.OriginProtocolPolicy.HTTPS_ONLY,
originReadTimeout: this.props.readTimeout?.toSeconds(),
originKeepaliveTimeout: this.props.keepaliveTimeout?.toSeconds(),
};
}
}
2 changes: 2 additions & 0 deletions packages/@aws-cdk/aws-cloudfront-origins/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
"aws-sdk": "^2.848.0"
},
"dependencies": {
"@aws-cdk/aws-apigateway": "0.0.0",
"@aws-cdk/aws-cloudfront": "0.0.0",
"@aws-cdk/aws-elasticloadbalancingv2": "0.0.0",
"@aws-cdk/aws-iam": "0.0.0",
Expand All @@ -95,6 +96,7 @@
},
"homepage": "https://github.com/aws/aws-cdk",
"peerDependencies": {
"@aws-cdk/aws-apigateway": "0.0.0",
"@aws-cdk/aws-cloudfront": "0.0.0",
"@aws-cdk/aws-elasticloadbalancingv2": "0.0.0",
"@aws-cdk/aws-iam": "0.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Fixture with packages imported, but nothing else
import { Duration, Stack } from '@aws-cdk/core';
import { Construct } from 'constructs';
import * as apigateway from '@aws-cdk/aws-apigateway';
import * as cloudfront from '@aws-cdk/aws-cloudfront';
import * as origins from '@aws-cdk/aws-cloudfront-origins';
import * as s3 from '@aws-cdk/aws-s3';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as apigateway from '@aws-cdk/aws-apigateway';
import * as cloudfront from '@aws-cdk/aws-cloudfront';
import * as cdk from '@aws-cdk/core';
import * as origins from '../lib';

const app = new cdk.App();

const stack = new cdk.Stack(app, 'integ-cloudfront-rest-api-origin');

const api = new apigateway.RestApi(stack, 'RestApi', { endpointTypes: [apigateway.EndpointType.REGIONAL] });
api.root.addMethod('GET');

new cloudfront.Distribution(stack, 'Distribution', {
defaultBehavior: { origin: new origins.RestApiOrigin(api) },
});

app.synth();
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"version":"18.0.0"}
Loading