Skip to content

Commit

Permalink
feat(cloudfront): REST API origin (#20335)
Browse files Browse the repository at this point in the history
Add an origin to work with API Gateway REST APIs.

Extracts the domain name and origin path from the API url.


----

### All Submissions:

* [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md)

### Adding new Unconventional Dependencies:

* [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md/#adding-new-unconventional-dependencies)

### New Features

* [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/master/INTEGRATION_TESTS.md)?
	* [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)?

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
jogold authored May 19, 2022
1 parent 6fae606 commit f7693e3
Show file tree
Hide file tree
Showing 15 changed files with 877 additions and 22 deletions.
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,
});

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

0 comments on commit f7693e3

Please sign in to comment.