@@ -3,8 +3,10 @@ addEventListener("fetch", (event) => {
33 event . respondWith ( handleRequest ( event . request ) ) ;
44} ) ;
55
6+ const dockerHub = "https://registry-1.docker.io" ;
7+
68const routes = {
7- "docker.libcuda.so" : "https://registry-1.docker.io" ,
9+ "docker.libcuda.so" : dockerHub ,
810 "quay.libcuda.so" : "https://quay.io" ,
911 "gcr.libcuda.so" : "https://gcr.io" ,
1012 "k8s-gcr.libcuda.so" : "https://k8s.gcr.io" ,
@@ -36,6 +38,7 @@ async function handleRequest(request) {
3638 }
3739 ) ;
3840 }
41+ const isDockerHub = upstream == dockerHub ;
3942 const authorization = request . headers . get ( "Authorization" ) ;
4043 if ( url . pathname == "/v2/" ) {
4144 const newUrl = new URL ( upstream + "/v2/" ) ;
@@ -84,7 +87,28 @@ async function handleRequest(request) {
8487 return resp ;
8588 }
8689 const wwwAuthenticate = parseAuthenticate ( authenticateStr ) ;
87- return await fetchToken ( wwwAuthenticate , url . searchParams , authorization ) ;
90+ let scope = url . searchParams . get ( "scope" ) ;
91+ // autocomplete repo part into scope for DockerHub library images
92+ // Example: repository:busybox:pull => repository:library/busybox:pull
93+ if ( scope && isDockerHub ) {
94+ let scopeParts = scope . split ( ":" ) ;
95+ if ( scopeParts . length == 3 && ! scopeParts [ 1 ] . includes ( "/" ) ) {
96+ scopeParts [ 1 ] = "library/" + scopeParts [ 1 ] ;
97+ scope = scopeParts . join ( ":" ) ;
98+ }
99+ }
100+ return await fetchToken ( wwwAuthenticate , scope , authorization ) ;
101+ }
102+ // redirect for DockerHub library images
103+ // Example: /v2/busybox/manifests/latest => /v2/library/busybox/manifests/latest
104+ if ( isDockerHub ) {
105+ const pathParts = url . pathname . split ( "/" ) ;
106+ if ( pathParts . length == 5 ) {
107+ pathParts . splice ( 2 , 0 , "library" ) ;
108+ const redirectUrl = new URL ( url ) ;
109+ redirectUrl . pathname = pathParts . join ( "/" ) ;
110+ return Response . redirect ( redirectUrl , 301 ) ;
111+ }
88112 }
89113 // foward requests
90114 const newUrl = new URL ( upstream + url . pathname ) ;
@@ -101,7 +125,7 @@ function parseAuthenticate(authenticateStr) {
101125 // match strings after =" and before "
102126 const re = / (?< = \= " ) (?: \\ .| [ ^ " \\ ] ) * (? = " ) / g;
103127 const matches = authenticateStr . match ( re ) ;
104- if ( matches === null || matches . length < 2 ) {
128+ if ( matches == null || matches . length < 2 ) {
105129 throw new Error ( `invalid Www-Authenticate Header: ${ authenticateStr } ` ) ;
106130 }
107131 return {
@@ -110,13 +134,13 @@ function parseAuthenticate(authenticateStr) {
110134 } ;
111135}
112136
113- async function fetchToken ( wwwAuthenticate , searchParams , authorization ) {
137+ async function fetchToken ( wwwAuthenticate , scope , authorization ) {
114138 const url = new URL ( wwwAuthenticate . realm ) ;
115139 if ( wwwAuthenticate . service . length ) {
116140 url . searchParams . set ( "service" , wwwAuthenticate . service ) ;
117141 }
118- if ( searchParams . get ( " scope" ) ) {
119- url . searchParams . set ( "scope" , searchParams . get ( " scope" ) ) ;
142+ if ( scope ) {
143+ url . searchParams . set ( "scope" , scope ) ;
120144 }
121145 headers = new Headers ( ) ;
122146 if ( authorization ) {
0 commit comments