From 7fbc5890950a4f4b48e0435d8d88314ff94d9b39 Mon Sep 17 00:00:00 2001 From: Yisheng Cai Date: Fri, 14 Jun 2024 01:46:36 +0800 Subject: [PATCH] Fix DockerHub library images default pulling case (#20) --- src/index.js | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/src/index.js b/src/index.js index 7b14b64eb..3f737321f 100644 --- a/src/index.js +++ b/src/index.js @@ -3,8 +3,10 @@ addEventListener("fetch", (event) => { event.respondWith(handleRequest(event.request)); }); +const dockerHub = "https://registry-1.docker.io"; + const routes = { - "docker.libcuda.so": "https://registry-1.docker.io", + "docker.libcuda.so": dockerHub, "quay.libcuda.so": "https://quay.io", "gcr.libcuda.so": "https://gcr.io", "k8s-gcr.libcuda.so": "https://k8s.gcr.io", @@ -36,6 +38,7 @@ async function handleRequest(request) { } ); } + const isDockerHub = upstream == dockerHub; const authorization = request.headers.get("Authorization"); if (url.pathname == "/v2/") { const newUrl = new URL(upstream + "/v2/"); @@ -84,7 +87,28 @@ async function handleRequest(request) { return resp; } const wwwAuthenticate = parseAuthenticate(authenticateStr); - return await fetchToken(wwwAuthenticate, url.searchParams, authorization); + let scope = url.searchParams.get("scope"); + // autocomplete repo part into scope for DockerHub library images + // Example: repository:busybox:pull => repository:library/busybox:pull + if (scope && isDockerHub) { + let scopeParts = scope.split(":"); + if (scopeParts.length == 3 && !scopeParts[1].includes("/")) { + scopeParts[1] = "library/" + scopeParts[1]; + scope = scopeParts.join(":"); + } + } + return await fetchToken(wwwAuthenticate, scope, authorization); + } + // redirect for DockerHub library images + // Example: /v2/busybox/manifests/latest => /v2/library/busybox/manifests/latest + if (isDockerHub) { + const pathParts = url.pathname.split("/"); + if (pathParts.length == 5) { + pathParts.splice(2, 0, "library"); + const redirectUrl = new URL(url); + redirectUrl.pathname = pathParts.join("/"); + return Response.redirect(redirectUrl, 301); + } } // foward requests const newUrl = new URL(upstream + url.pathname); @@ -101,7 +125,7 @@ function parseAuthenticate(authenticateStr) { // match strings after =" and before " const re = /(?<=\=")(?:\\.|[^"\\])*(?=")/g; const matches = authenticateStr.match(re); - if (matches === null || matches.length < 2) { + if (matches == null || matches.length < 2) { throw new Error(`invalid Www-Authenticate Header: ${authenticateStr}`); } return { @@ -110,13 +134,13 @@ function parseAuthenticate(authenticateStr) { }; } -async function fetchToken(wwwAuthenticate, searchParams, authorization) { +async function fetchToken(wwwAuthenticate, scope, authorization) { const url = new URL(wwwAuthenticate.realm); if (wwwAuthenticate.service.length) { url.searchParams.set("service", wwwAuthenticate.service); } - if (searchParams.get("scope")) { - url.searchParams.set("scope", searchParams.get("scope")); + if (scope) { + url.searchParams.set("scope", scope); } headers = new Headers(); if (authorization) {