Skip to content
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
2 changes: 2 additions & 0 deletions handleRequest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ it("should redirect non-wasm plugins to asset URL", async () => {
expect(response.headers.get("location")).toEqual(
"https://plugins.dprint.dev/dprint/dprint-plugin-prettier/0.7.0/asset/plugin.json",
);
expect(response.headers.get("access-control-allow-origin")).toEqual("https://dprint.dev");
});

it("should not redirect wasm plugins", async () => {
Expand All @@ -354,6 +355,7 @@ it("should redirect non-allowed org asset to GitHub", async () => {
expect(response.headers.get("location")).toEqual(
"https://github.com/someone/some-repo/releases/download/0.1.0/file.zip",
);
expect(response.headers.get("access-control-allow-origin")).toEqual("https://dprint.dev");
});

it("should return 404 for asset not found", async () => {
Expand Down
20 changes: 18 additions & 2 deletions handleRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function createRequestHandler() {
const assetResult = tryResolveAssetUrl(url);
if (assetResult != null) {
if (!assetResult.shouldCache) {
return Response.redirect(assetResult.githubUrl, 302);
return createRedirectResponse(request, assetResult.githubUrl);
}
return servePlugin(request, assetResult.githubUrl, ctx);
}
Expand All @@ -47,7 +47,7 @@ export function createRequestHandler() {
// plugin.json files resolve correctly
const assetPath = githubUrlToAssetPath(githubUrl);
if (assetPath != null) {
return Response.redirect(`${url.origin}${assetPath}`, 302);
return createRedirectResponse(request, `${url.origin}${assetPath}`);
}
}
return servePlugin(request, githubUrl, ctx);
Expand Down Expand Up @@ -124,6 +124,7 @@ export function createRequestHandler() {
headers: {
"content-type": result.contentType,
"Access-Control-Allow-Origin": getAccessControlAllowOrigin(request),
"Vary": "Origin",
},
status: result.status,
});
Expand Down Expand Up @@ -209,6 +210,20 @@ export async function resolvePluginOrSchemaUrl(url: URL) {
return await tryResolveSchemaUrl(url);
}

// builds a redirect that also carries the CORS header, because for
// cross-origin fetches the browser runs the CORS check on the redirect
// response itself — not just the final response it points at
function createRedirectResponse(request: Request, location: string) {
return new Response(null, {
status: 302,
headers: {
"location": location,
"Access-Control-Allow-Origin": getAccessControlAllowOrigin(request),
"Vary": "Origin",
},
});
}

function getAccessControlAllowOrigin(request: Request) {
const origin = request.headers.get("origin");
return origin != null && isLocalHostname(new URL(origin).hostname)
Expand All @@ -225,6 +240,7 @@ function createJsonResponse(text: string, request: Request) {
headers: {
"content-type": contentTypes.json,
"Access-Control-Allow-Origin": getAccessControlAllowOrigin(request),
"Vary": "Origin",
},
status: 200,
});
Expand Down
Loading