Skip to content
This repository has been archived by the owner on Jan 28, 2025. It is now read-only.

Commit

Permalink
feat(lambda-at-edge): use S3 regional endpoint when not in us-east-1 (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
danielcondemarin authored Jun 30, 2020
1 parent f422d7f commit 5ecff1a
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 73 deletions.
30 changes: 23 additions & 7 deletions packages/lambda-at-edge/src/default-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,24 @@ const addS3HostHeader = (

const isDataRequest = (uri: string): boolean => uri.startsWith("/_next/data");

const normaliseUri = (uri: string): string => (uri === "/" ? "/index" : uri);

const normaliseS3OriginDomain = (s3Origin: CloudFrontS3Origin): string => {
if (s3Origin.region === "us-east-1") {
return s3Origin.domainName;
}

if (!s3Origin.domainName.includes(s3Origin.region)) {
const regionalEndpoint = s3Origin.domainName.replace(
"s3.amazonaws.com",
`s3.${s3Origin.region}.amazonaws.com`
);
return regionalEndpoint;
}

return s3Origin.domainName;
};

const router = (
manifest: OriginRequestDefaultHandlerManifest
): ((uri: string) => string) => {
Expand Down Expand Up @@ -66,8 +84,6 @@ const router = (
};
};

const normaliseUri = (uri: string): string => (uri === "/" ? "/index" : uri);

export const handler = async (
event: OriginRequestEvent
): Promise<CloudFrontResultResponse | CloudFrontRequest> => {
Expand All @@ -76,21 +92,21 @@ export const handler = async (
const manifest: OriginRequestDefaultHandlerManifest = Manifest;
const prerenderManifest: PrerenderManifestType = PrerenderManifest;
const { pages, publicFiles } = manifest;

const isStaticPage = pages.html.nonDynamic[uri];
const isPublicFile = publicFiles[uri];
const isPrerenderedPage = prerenderManifest.routes[request.uri]; // prerendered pages are also static pages like "pages.html" above, but are defined in the prerender-manifest

const origin = request.origin as CloudFrontOrigin;
const s3Origin = origin.s3 as CloudFrontS3Origin;

const isHTMLPage = isStaticPage || isPrerenderedPage;
const normalisedS3DomainName = normaliseS3OriginDomain(s3Origin);

s3Origin.domainName = normalisedS3DomainName;

if (isHTMLPage || isPublicFile) {
s3Origin.path = isHTMLPage ? "/static-pages" : "/public";

if (isHTMLPage) {
addS3HostHeader(request, s3Origin.domainName);
addS3HostHeader(request, normalisedS3DomainName);
request.uri = `${uri}.html`;
}

Expand All @@ -102,7 +118,7 @@ export const handler = async (
if (pagePath.endsWith(".html")) {
s3Origin.path = "/static-pages";
request.uri = pagePath.replace("pages", "");
addS3HostHeader(request, s3Origin.domainName);
addS3HostHeader(request, normalisedS3DomainName);
return request;
}

Expand Down
4 changes: 2 additions & 2 deletions packages/lambda-at-edge/tests/api-handler/api-handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe("API lambda handler", () => {
host: "mydistribution.cloudfront.net",
origin: {
s3: {
domainName: "my-bucket.amazonaws.com"
domainName: "my-bucket.s3.amazonaws.com"
}
}
});
Expand All @@ -48,7 +48,7 @@ describe("API lambda handler", () => {
host: "mydistribution.cloudfront.net",
origin: {
s3: {
domainName: "my-bucket.amazonaws.com"
domainName: "my-bucket.s3.amazonaws.com"
}
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe("Lambda@Edge", () => {
host: "mydistribution.cloudfront.net",
origin: {
s3: {
domainName: "my-bucket.amazonaws.com"
domainName: "my-bucket.s3.amazonaws.com"
}
}
});
Expand Down
145 changes: 89 additions & 56 deletions packages/lambda-at-edge/tests/default-handler/default-handler.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { handler } from "../../src/default-handler";
import { createCloudFrontEvent } from "../test-utils";
import { CloudFrontRequest, CloudFrontResultResponse } from "aws-lambda";
import {
CloudFrontRequest,
CloudFrontResultResponse,
CloudFrontHeaders,
CloudFrontOrigin
} from "aws-lambda";

jest.mock(
"../../src/manifest.json",
Expand Down Expand Up @@ -45,23 +50,19 @@ describe("Lambda@Edge", () => {
async ({ path, expectedPage }) => {
const event = createCloudFrontEvent({
uri: path,
host: "mydistribution.cloudfront.net",
origin: {
s3: {
authMethod: "origin-access-identity",
domainName: "my-bucket.s3.amazonaws.com",
path: ""
}
}
host: "mydistribution.cloudfront.net"
});

const request = await handler(event);
const result = await handler(event);

const request = result as CloudFrontRequest;

expect(request.origin).toEqual({
s3: {
authMethod: "origin-access-identity",
domainName: "my-bucket.s3.amazonaws.com",
path: "/static-pages"
path: "/static-pages",
region: "us-east-1"
}
});
expect(request.uri).toEqual(expectedPage);
Expand All @@ -77,27 +78,22 @@ describe("Lambda@Edge", () => {
it("serves public file from S3 /public folder", async () => {
const event = createCloudFrontEvent({
uri: "/manifest.json",
host: "mydistribution.cloudfront.net",
origin: {
s3: {
authMethod: "origin-access-identity",
domainName: "my-bucket.s3.amazonaws.com",
path: ""
}
}
host: "mydistribution.cloudfront.net"
});

const request = await handler(event);
const result = await handler(event);

const cfRequest = request as CloudFrontRequest;
expect(cfRequest.origin).toEqual({
const request = result as CloudFrontRequest;

expect(request.origin).toEqual({
s3: {
authMethod: "origin-access-identity",
domainName: "my-bucket.s3.amazonaws.com",
path: "/public"
path: "/public",
region: "us-east-1"
}
});
expect(cfRequest.uri).toEqual("/manifest.json");
expect(request.uri).toEqual("/manifest.json");
});
});

Expand All @@ -116,22 +112,18 @@ describe("Lambda@Edge", () => {
async ({ path, expectedPage }) => {
const event = createCloudFrontEvent({
uri: path,
host: "mydistribution.cloudfront.net",
origin: {
s3: {
domainName: "my-bucket.amazonaws.com"
}
}
host: "mydistribution.cloudfront.net"
});

mockPageRequire(expectedPage);

const response = await handler(event);

const cfResponse = response as CloudFrontResultResponse;
const decodedBody = new Buffer(cfResponse.body, "base64").toString(
"utf8"
);
const decodedBody = new Buffer(
cfResponse.body as string,
"base64"
).toString("utf8");

expect(decodedBody).toEqual(expectedPage);
expect(cfResponse.status).toEqual(200);
Expand All @@ -148,50 +140,91 @@ describe("Lambda@Edge", () => {
`("serves json data for path $path", async ({ path, expectedPage }) => {
const event = createCloudFrontEvent({
uri: path,
host: "mydistribution.cloudfront.net",
origin: {
s3: {
domainName: "my-bucket.amazonaws.com"
}
}
host: "mydistribution.cloudfront.net"
});

mockPageRequire(expectedPage);

const response = await handler(event);
const result = await handler(event);

const cfResponse = response as CloudFrontResultResponse;
const decodedBody = new Buffer(cfResponse.body, "base64").toString(
"utf8"
);
const response = result as CloudFrontResultResponse;
const decodedBody = new Buffer(
response.body as string,
"base64"
).toString("utf8");

expect(cfResponse.headers["content-type"][0].value).toEqual(
"application/json"
);
const headers = response.headers as CloudFrontHeaders;
expect(headers["content-type"][0].value).toEqual("application/json");
expect(JSON.parse(decodedBody)).toEqual({
page: expectedPage
});
expect(cfResponse.status).toEqual(200);
expect(response.status).toEqual(200);
});
});

it("uses default s3 endpoint when bucket region is us-east-1", async () => {
const event = createCloudFrontEvent({
uri: "/terms",
host: "mydistribution.cloudfront.net",
s3Region: "us-east-1"
});

const result = await handler(event);

const request = result as CloudFrontRequest;
const origin = request.origin as CloudFrontOrigin;

expect(origin.s3).toEqual(
expect.objectContaining({
domainName: "my-bucket.s3.amazonaws.com"
})
);
expect(request.headers.host[0].key).toEqual("host");
expect(request.headers.host[0].value).toEqual(
"my-bucket.s3.amazonaws.com"
);
});

it("uses regional endpoint when bucket region is not us-east-1", async () => {
const event = createCloudFrontEvent({
uri: "/terms",
host: "mydistribution.cloudfront.net",
s3DomainName: "my-bucket.s3.amazonaws.com",
s3Region: "eu-west-1"
});

const result = await handler(event);

const request = result as CloudFrontRequest;
const origin = request.origin as CloudFrontOrigin;

expect(origin).toEqual({
s3: {
authMethod: "origin-access-identity",
domainName: "my-bucket.s3.eu-west-1.amazonaws.com",
path: "/static-pages",
region: "eu-west-1"
}
});
expect(request.uri).toEqual("/terms.html");
expect(request.headers.host[0].key).toEqual("host");
expect(request.headers.host[0].value).toEqual(
"my-bucket.s3.eu-west-1.amazonaws.com"
);
});
});

it("renders 404 page if request path can't be matched to any page / api routes", async () => {
const event = createCloudFrontEvent({
uri: "/page/does/not/exist",
host: "mydistribution.cloudfront.net",
origin: {
s3: {
domainName: "my-bucket.amazonaws.com"
}
}
host: "mydistribution.cloudfront.net"
});

mockPageRequire("pages/_error.js");

const response = (await handler(event)) as CloudFrontResultResponse;

const decodedBody = new Buffer(response.body, "base64").toString("utf8");
const body = response.body as string;
const decodedBody = new Buffer(body, "base64").toString("utf8");

expect(decodedBody).toEqual("pages/_error.js");
expect(response.status).toEqual(200);
Expand Down
25 changes: 18 additions & 7 deletions packages/lambda-at-edge/tests/test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,19 @@ export const removeNewLineChars = (text: string): string =>
export const getNextBinary = (): string =>
path.join(require.resolve("next"), "../../../../.bin/next");

type CloudFrontEventOptions = {
uri: string;
host: string;
s3DomainName?: string;
s3Region?: string;
};

export const createCloudFrontEvent = ({
uri,
host,
origin
}: {
uri: string;
host: string;
origin: CloudFrontOrigin;
}): OriginRequestEvent => ({
s3DomainName,
s3Region
}: CloudFrontEventOptions): OriginRequestEvent => ({
Records: [
{
cf: {
Expand All @@ -38,7 +42,14 @@ export const createCloudFrontEvent = ({
}
]
},
origin
origin: {
s3: {
path: "",
region: s3Region || "us-east-1",
authMethod: "origin-access-identity",
domainName: s3DomainName || "my-bucket.s3.amazonaws.com"
}
}
}
}
}
Expand Down

0 comments on commit 5ecff1a

Please sign in to comment.