Skip to content
Open
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
4 changes: 2 additions & 2 deletions platform/src/components/aws/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2755,7 +2755,7 @@ async function routeSite(kvNamespace, metadata) {
: ["", ".html", "/index.html"];
const v = await Promise.any(postfixes.map(p => cf.kvs().get(kvNamespace + ":" + u + p).then(v => p)));
// files are stored in a subdirectory, add it to the request uri
event.request.uri = metadata.s3.dir + event.request.uri + v;
event.request.uri = metadata.s3.dir + baselessUri + v;
setS3Origin(metadata.s3.domain);
return;
} catch (e) {}
Expand All @@ -2765,7 +2765,7 @@ async function routeSite(kvNamespace, metadata) {
for (var i=0, l=metadata.s3.routes.length; i<l; i++) {
const route = metadata.s3.routes[i];
if (baselessUri.startsWith(route)) {
event.request.uri = metadata.s3.dir + event.request.uri;
event.request.uri = metadata.s3.dir + baselessUri;
// uri ends with /, ie. /usage/ -> /usage/index.html
if (event.request.uri.endsWith("/")) {
event.request.uri += "index.html";
Expand Down
118 changes: 112 additions & 6 deletions platform/test/components/cloudfront.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,7 @@ function createContext(input: CreateContextInput) {
return { context, event };
}

function loadRouteSite(input: {
uri: string;
headers: Record<string, CloudFrontField>;
cookies?: Record<string, CloudFrontField>;
querystring?: Record<string, unknown>;
}) {
function loadRouteSite(input: CreateContextInput) {
const { context, event } = createContext(input);

new vm.Script(
Expand Down Expand Up @@ -465,4 +460,115 @@ describe("CloudFront router", () => {
expect(response.body.data).toContain("Reduce cookie size");
});
});

describe("base path", () => {
const S3_DOMAIN = "assets.s3.us-east-1.amazonaws.com";

function siteMetadata(overrides: Record<string, any> = {}) {
return {
base: "/admin/board",
s3: { domain: S3_DOMAIN, dir: "/_assets", routes: ["/_next/static"] },
servers: [["server.lambda-url.us-east-1.on.aws", 0, 0]],
...overrides,
};
}

it("keys an S3 route without the base", async () => {
// The regression. The route is matched against the baseless uri, so the
// key has to be built from it too. Building it from the full uri asks S3
// for /_assets/admin/board/_next/... while the deploy uploaded
// /_assets/_next/..., so every asset 404s. S3 reports that as 403 when
// the caller cannot list, which sends people hunting for a permissions
// problem that is not there.
const { event, routeSite } = loadRouteSite({
uri: "/admin/board/_next/static/css/abc123.css",
headers: { host: { value: "example.com" } },
});

await routeSite("test", siteMetadata());

expect(event.request.uri).toBe("/_assets/_next/static/css/abc123.css");
});

it("does not leave the base anywhere in the key", async () => {
const { event, routeSite } = loadRouteSite({
uri: "/admin/board/_next/static/chunk.js",
headers: { host: { value: "example.com" } },
});

await routeSite("test", siteMetadata());

expect(event.request.uri).not.toContain("/admin/board");
});

it("sends it to the S3 origin", async () => {
let origin: any;
const { routeSite } = loadRouteSite({
uri: "/admin/board/_next/static/chunk.js",
headers: { host: { value: "example.com" } },
updateRequestOrigin: (o) => (origin = o),
});

await routeSite("test", siteMetadata());

expect(origin.domainName).toBe(S3_DOMAIN);
});

it("is unchanged for a site with no base", async () => {
const { event, routeSite } = loadRouteSite({
uri: "/_next/static/css/abc123.css",
headers: { host: { value: "example.com" } },
});

await routeSite("test", siteMetadata({ base: undefined }));

expect(event.request.uri).toBe("/_assets/_next/static/css/abc123.css");
});

it("keys a file found in the KV store without the base", async () => {
// The other branch that matches baseless and then keyed with the full
// uri. Its own comment says files are stored in the root.
const { event, routeSite } = loadRouteSite({
uri: "/admin/board/about",
headers: { host: { value: "example.com" } },
kvGet: async (key: string) => {
if (key === "test:/about.html") return "";
throw new Error("missing");
},
});

await routeSite("test", siteMetadata());

expect(event.request.uri).toBe("/_assets/about.html");
});

it("still appends index.html under a base", async () => {
const { event, routeSite } = loadRouteSite({
uri: "/admin/board/docs/guide",
headers: { host: { value: "example.com" } },
});

await routeSite(
"test",
siteMetadata({
s3: { domain: S3_DOMAIN, dir: "/_assets", routes: ["/docs"] },
}),
);

expect(event.request.uri).toBe("/_assets/docs/guide/index.html");
});

it("leaves a path the S3 routes do not match to the server", async () => {
let origin: any;
const { routeSite } = loadRouteSite({
uri: "/admin/board/b/123",
headers: { host: { value: "example.com" } },
updateRequestOrigin: (o) => (origin = o),
});

await routeSite("test", siteMetadata());

expect(origin.domainName).toBe("server.lambda-url.us-east-1.on.aws");
});
});
});