Simulated CloudFront
Simulated CloudFront
Section titled “Simulated CloudFront”Yulin includes a simulated CloudFront service for tests and local development.
Sim CloudFront can be used directly through SimAws, and it can also be served on localhost
alongside other simulated AWS services. This is most useful when you want application code to make
HTTP requests through a CloudFront-like layer without talking to real AWS.
Alternatively, you can also instantiate SimCloudFront directly on its own, in which case it has
its own isolated state that is not connected to a wider simulated AWS environment.
Available functionality
Section titled “Available functionality”Sim CloudFront currently supports:
- Creating Distributions with
CreateDistributionCommand - Getting Distributions with
GetDistributionCommand - S3 Origins backed by sim S3 Buckets
- CloudFront Distribution hostnames such as
distro123.cloudfront.net - Default cache Behavior and path-based cache Behaviors
viewer-requestandviewer-responseCloudFront Functions- Serving simulated CloudFront traffic on localhost with
serveSimAws
The simulator focuses on useful behavior for isolated tests and local dev rather than full CloudFront feature parity. Unsupported CloudFront options may be ignored or may throw errors depending on whether the simulator needs them to model the requested behaviour safely.
Basic Distribution setup
Section titled “Basic Distribution setup”Create a simulated AWS environment, add a sim S3 Bucket, and create a sim CloudFront Distribution pointing at that Bucket.
/** * Creating a simulated CloudFront Distribution with a simulated S3 Origin. */
import { CreateDistributionCommand } from "@aws-sdk/client-cloudfront";import { CreateBucketCommand } from "@aws-sdk/client-s3";
import { SimAws } from "@kensio/yulin";
const simAws = new SimAws();const simS3 = simAws.s3();const simCloudFront = simAws.cloudFront();
await simS3.createBucket( new CreateBucketCommand({ Bucket: "foo-bucket", }),);
const createDistroOut = await simCloudFront.createDistribution( new CreateDistributionCommand({ DistributionConfig: { CallerReference: "assets-cdn", Comment: "Assets CDN", Enabled: true, Origins: { Quantity: 1, Items: [ { Id: "assets-origin", DomainName: "foo-bucket.s3.amazonaws.com", S3OriginConfig: { OriginAccessIdentity: "", }, }, ], }, DefaultCacheBehavior: { TargetOriginId: "assets-origin", ViewerProtocolPolicy: "allow-all", }, }, }),);
console.log(createDistroOut.Distribution?.DomainName);Serve simulated CloudFront on localhost
Section titled “Serve simulated CloudFront on localhost”Use serveSimAws when you want to make real HTTP requests to the simulated system on localhost.
/** * Serving a simulated CloudFront Distribution on localhost. */
import { CreateDistributionCommand } from "@aws-sdk/client-cloudfront";import { CreateBucketCommand, PutObjectCommand } from "@aws-sdk/client-s3";
import { SimAws } from "@kensio/yulin";import { serveSimAws } from "@kensio/yulin/serve";
const simAws = new SimAws();const srv = await serveSimAws({ simAws });
try { const simS3 = simAws.s3(); const simCloudFront = simAws.cloudFront();
await simS3.createBucket( new CreateBucketCommand({ Bucket: "foo-bucket", }), );
await simS3.putObject( new PutObjectCommand({ Bucket: "foo-bucket", Key: "hello.txt", Body: "Hello from simulated CloudFront", }), );
const createDistributionOutput = await simCloudFront.createDistribution( new CreateDistributionCommand({ DistributionConfig: { CallerReference: "localhost-assets-cdn", Comment: "Localhost Assets CDN", Enabled: true, Origins: { Quantity: 1, Items: [ { Id: "assets-origin", DomainName: "foo-bucket.s3.amazonaws.com", S3OriginConfig: { OriginAccessIdentity: "", }, }, ], }, DefaultCacheBehavior: { TargetOriginId: "assets-origin", ViewerProtocolPolicy: "allow-all", }, }, }), );
const distroHostname = createDistributionOutput.Distribution?.DomainName;
if (distroHostname === undefined) { throw new Error("Expected sim CloudFront Distribution hostname"); }
const url = srv.localUrl(`http://${distroHostname}/hello.txt`); const response = await fetch(url);
console.log(response.status); console.log(await response.text());} finally { srv.close();}The Distribution domain is adapted through server.localUrl(...) so that the request is sent to the
local Yulin server while preserving the simulated CloudFront hostname.
Simulated CloudFront Functions
Section titled “Simulated CloudFront Functions”The sim CloudFront supports viewer-request and viewer-response CloudFront Functions.
Use makeCffFunctionCodeInput to pass a JavaScript handler function to CreateFunctionCommand.
/** * Simulated CloudFront Functions. */
import { CreateDistributionCommand, CreateFunctionCommand,} from "@aws-sdk/client-cloudfront";import { CreateBucketCommand } from "@aws-sdk/client-s3";
import { SimAws } from "@kensio/yulin";import { makeCffFunctionCodeInput, type CloudFrontFunction,} from "@kensio/yulin/cloudfront";import { serveSimAws } from "@kensio/yulin/serve";
const simAws = new SimAws();const srv = await serveSimAws({ simAws });
try { const simS3 = simAws.s3(); const simCloudFront = simAws.cloudFront();
await simS3.createBucket( new CreateBucketCommand({ Bucket: "foo-bucket", }), );
function viewerRequestFunction( event: CloudFrontFunction.ViewerRequestEvent, ): CloudFrontFunction.Request | CloudFrontFunction.Response { if (event.request.uri === "/old-page.html") { return { statusCode: 302, statusDescription: "Found", headers: { location: { value: "https://example.test/new-page.html", }, }, }; }
return event.request; }
const createFunctionOutput = await simCloudFront.createFunction( new CreateFunctionCommand({ Name: "redirect-old-page", FunctionConfig: { Comment: "Redirect old page", Runtime: "cloudfront-js-2.0", }, FunctionCode: makeCffFunctionCodeInput(viewerRequestFunction), }), );
const createDistributionOutput = await simCloudFront.createDistribution( new CreateDistributionCommand({ DistributionConfig: { CallerReference: "function-cdn", Comment: "Function CDN", Enabled: true, Origins: { Quantity: 1, Items: [ { Id: "assets-origin", DomainName: "foo-bucket.s3.amazonaws.com", S3OriginConfig: { OriginAccessIdentity: "", }, }, ], }, DefaultCacheBehavior: { TargetOriginId: "assets-origin", ViewerProtocolPolicy: "allow-all", FunctionAssociations: { Quantity: 1, Items: [ { EventType: "viewer-request", FunctionARN: createFunctionOutput.FunctionMetadata.FunctionARN, }, ], }, }, }, }), );
const distroHostname = createDistributionOutput.Distribution?.DomainName;
if (distroHostname === undefined) { throw new Error("Expected sim CloudFront Distribution hostname"); }
const url = srv.localUrl(`http://${distroHostname}/old-page.html`); const response = await fetch(url, { redirect: "manual" });
console.log(response.status); console.log(response.headers.get("location"));} finally { srv.close();}