diff --git a/src/main/java/land/oras/Registry.java b/src/main/java/land/oras/Registry.java index 3b9d4dd..89206d9 100644 --- a/src/main/java/land/oras/Registry.java +++ b/src/main/java/land/oras/Registry.java @@ -475,6 +475,7 @@ private boolean hasBlob(ContainerRef containerRef) { private HttpClient.ResponseWrapper headBlob(ContainerRef containerRef) { URI uri = URI.create( "%s://%s".formatted(getScheme(), containerRef.forRegistry(this).getBlobsPath(this))); + LOG.debug("Checking blob existence with HEAD request to: {}", uri); HttpClient.ResponseWrapper response = client.head( uri, Map.of(Const.ACCEPT_HEADER, Const.APPLICATION_OCTET_STREAM_HEADER_VALUE), @@ -561,8 +562,19 @@ public Manifest getManifest(ContainerRef containerRef) { throw new OrasException( "Expected manifest but got index. Probably a multi-platform image instead of artifact"); } - ManifestDescriptor manifestDescriptor = ManifestDescriptor.of(descriptor); - return Manifest.fromJson(descriptor.getJson()).withDescriptor(manifestDescriptor); + String json = descriptor.getJson(); + String digest = descriptor.getDigest(); + if (digest == null) { + LOG.debug("Digest missing from header, using from reference"); + digest = containerRef.getDigest(); + if (digest == null) { + LOG.debug("Digest missing from reference, computing from content"); + digest = containerRef.getAlgorithm().digest(json.getBytes(StandardCharsets.UTF_8)); + LOG.debug("Computed index digest: {}", digest); + } + } + ManifestDescriptor manifestDescriptor = ManifestDescriptor.of(descriptor, digest); + return Manifest.fromJson(json).withDescriptor(manifestDescriptor); } @Override @@ -734,9 +746,13 @@ private void handleError(HttpClient.ResponseWrapper responseWrapper) { private void logResponse(HttpClient.ResponseWrapper response) { LOG.debug("Status Code: {}", response.statusCode()); LOG.debug("Headers: {}", response.headers()); + String contentType = response.headers().get(Const.CONTENT_TYPE_HEADER.toLowerCase()); + boolean isBinaryResponse = contentType != null && contentType.contains("octet-stream"); // Only log non-binary responses - if (response.response() instanceof String) { + if (response.response() instanceof String && !isBinaryResponse) { LOG.debug("Response: {}", response.response()); + } else { + LOG.debug("Not logging binary response of content type: {}", contentType); } } diff --git a/src/main/java/land/oras/auth/HttpClient.java b/src/main/java/land/oras/auth/HttpClient.java index 6e3c68c..9a89b29 100644 --- a/src/main/java/land/oras/auth/HttpClient.java +++ b/src/main/java/land/oras/auth/HttpClient.java @@ -533,6 +533,10 @@ private ResponseWrapper redoRequest( } try { builder = builder.setHeader(Const.AUTHORIZATION_HEADER, "Bearer " + bearerToken); + if (builder.build().method() == "HEAD") { + LOG.debug("Redoing HEAD request to GET with new token"); + builder = builder.GET(); + } HttpResponse newResponse = client.send(builder.build(), handler); // Follow redirect diff --git a/src/test/java/land/oras/PublicECRITCase.java b/src/test/java/land/oras/PublicECRITCase.java index 0de3a11..192963e 100644 --- a/src/test/java/land/oras/PublicECRITCase.java +++ b/src/test/java/land/oras/PublicECRITCase.java @@ -26,7 +26,7 @@ import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; -@Execution(ExecutionMode.CONCURRENT) +@Execution(ExecutionMode.SAME_THREAD) // Avoid 429 Too Many Requests for unauthenticated requests to public ECR class PublicECRITCase { @Test @@ -44,4 +44,21 @@ void shouldPullAnonymousIndex() { Index index2 = registry.getIndex(containerRef2); assertNotNull(index2); } + + @Test + void shouldPullManifest() { + Registry registry = Registry.builder().build(); + ContainerRef containerRef = ContainerRef.parse( + "public.ecr.aws/docker/library/alpine@sha256:59855d3dceb3ae53991193bd03301e082b2a7faa56a514b03527ae0ec2ce3a95"); + Manifest manifest = registry.getManifest(containerRef); + assertNotNull(manifest); + } + + @Test + void shouldPullLayer() { + Registry registry = Registry.builder().build(); + ContainerRef containerRef = ContainerRef.parse( + "public.ecr.aws/docker/library/alpine@sha256:589002ba0eaed121a1dbf42f6648f29e5be55d5c8a6ee0f8eaa0285cc21ac153"); + registry.getBlob(containerRef); + } }