@@ -55,7 +55,7 @@ extension RegistryClient {
5555 }
5656
5757 guard let digest = response. headers. first ( name: " Docker-Content-Digest " ) else {
58- throw ContainerizationError ( . invalidArgument , message : " missing required header Docker-Content-Digest " )
58+ return try await resolveByFetchingManifest ( name : name , tag : tag , headers : headers )
5959 }
6060
6161 guard let type = response. headers. first ( name: " Content-Type " ) else {
@@ -74,6 +74,41 @@ extension RegistryClient {
7474 }
7575 }
7676
77+ /// Resolve a root manifest descriptor by fetching the manifest when the registry does not
78+ /// include a Docker-Content-Digest header in the HEAD response.
79+ private func resolveByFetchingManifest(
80+ name: String ,
81+ tag: String ,
82+ headers: [ ( String , String ) ]
83+ ) async throws -> Descriptor {
84+ var components = base
85+ components. path = " /v2/ \( name) /manifests/ \( tag) "
86+
87+ return try await request ( components: components, headers: headers) { response in
88+ guard response. status == . ok else {
89+ let url = components. url? . absoluteString ?? " unknown "
90+ let reason = await ErrorResponse . fromResponseBody ( response. body) ? . jsonString
91+ throw Error . invalidStatus ( url: url, response. status, reason: reason)
92+ }
93+
94+ guard let type = response. headers. first ( name: " Content-Type " ) else {
95+ throw ContainerizationError ( . invalidArgument, message: " missing required header Content-Type " )
96+ }
97+
98+ let buffer = try await response. body. collect ( upTo: . max)
99+ let data = Data ( buffer. readableBytesView)
100+
101+ let computedDigest = SHA256 . hash ( data: data)
102+ let digestString = computedDigest. map { String ( format: " %02x " , $0) } . joined ( )
103+
104+ return Descriptor (
105+ mediaType: type,
106+ digest: " sha256: \( digestString) " ,
107+ size: Int64 ( data. count)
108+ )
109+ }
110+ }
111+
77112 /// Fetch resource (either manifest or blob) to memory with JSON decoding.
78113 public func fetch< T: Codable > ( name: String , descriptor: Descriptor ) async throws -> T {
79114 var components = base
0 commit comments