@@ -11,7 +11,7 @@ import {
1111} from "../chunk" ;
1212import { InternalError , ManifestError , RangeError , ServerError } from "../errors" ;
1313import { SHA256_PREFIX_LEN , getSHA256 , hexToDigest } from "../user" ;
14- import { readableToBlob , readerToBlob , wrap } from "../utils" ;
14+ import { errorString , readableToBlob , readerToBlob , wrap } from "../utils" ;
1515import { BlobUnknownError , ManifestUnknownError } from "../v2-errors" ;
1616import {
1717 CheckLayerResponse ,
@@ -29,6 +29,7 @@ import {
2929} from "./registry" ;
3030import { GarbageCollectionMode , GarbageCollector } from "./garbage-collector" ;
3131import { ManifestSchema , manifestSchema } from "../manifest" ;
32+ import { DigestInvalid , RegistryResponseJSON } from "../v2-responses" ;
3233
3334export type Chunk =
3435 | {
@@ -691,12 +692,27 @@ export class R2Registry implements Registry {
691692 // TODO: Handle one last buffer here
692693 await upload . complete ( state . parts ) ;
693694 const obj = await this . env . REGISTRY . get ( uuid ) ;
694- const put = this . env . REGISTRY . put ( `${ namespace } /blobs/${ expectedSha } ` , obj ! . body , {
695- sha256 : ( expectedSha as string ) . slice ( SHA256_PREFIX_LEN ) ,
696- } ) ;
695+ if ( ! obj ) throw new Error ( "internal error, upload not found just after completing" ) ;
696+ if ( obj . size >= 2 * 1000 * 1000 ) {
697+ const digestStream = new crypto . DigestStream ( "SHA-256" ) ;
698+ await obj . body . pipeTo ( digestStream ) ;
699+ const digestHex = await digestStream . digest ;
700+ const digest = hexToDigest ( digestHex ) ;
701+ if ( digest !== expectedSha ) {
702+ return { response : new RegistryResponseJSON ( JSON . stringify ( DigestInvalid ( expectedSha , digest ) ) ) } ;
703+ }
704+ } else {
705+ const put = this . env . REGISTRY . put ( `${ namespace } /blobs/${ expectedSha } ` , obj ! . body , {
706+ sha256 : ( expectedSha as string ) . slice ( SHA256_PREFIX_LEN ) ,
707+ } ) ;
697708
698- await put ;
699- await this . env . REGISTRY . delete ( uuid ) ;
709+ const [ , err ] = await wrap < unknown , Error > ( put ) ;
710+ if ( err !== null ) {
711+ return { response : handleR2PutError ( expectedSha , err ) } ;
712+ }
713+
714+ await this . env . REGISTRY . delete ( uuid ) ;
715+ }
700716 }
701717
702718 await this . env . REGISTRY . delete ( getRegistryUploadsPath ( state ) ) ;
@@ -752,3 +768,28 @@ export class R2Registry implements Registry {
752768 return result ;
753769 }
754770}
771+
772+ function handleR2PutError ( expectedSha : string , err : unknown ) : Response {
773+ if ( err instanceof Error ) {
774+ // this is very annoying, parsing the error message manually to know the error and retrieve the calculated hash...
775+ if (
776+ err instanceof Error &&
777+ err . message . includes ( "The SHA-256 checksum you specified did not match what we received" )
778+ ) {
779+ const actualHashWasMessage = "Actual SHA-256 was: " ;
780+ const digestIndex = err . message . indexOf ( actualHashWasMessage ) ;
781+ const errorCodeIndex = err . message . indexOf ( " (10037)" ) ;
782+ let hash = "" ;
783+ if ( digestIndex !== - 1 && errorCodeIndex !== - 1 && digestIndex < errorCodeIndex ) {
784+ hash = "sha256:" + err . message . slice ( actualHashWasMessage . length + digestIndex , errorCodeIndex ) ;
785+ }
786+
787+ return new RegistryResponseJSON ( JSON . stringify ( DigestInvalid ( hash , expectedSha ) ) , {
788+ status : 400 ,
789+ } ) ;
790+ }
791+ }
792+
793+ console . error ( `There has been an internal error pushing ${ expectedSha } : ${ errorString ( err ) } ` ) ;
794+ return new RegistryResponseJSON ( JSON . stringify ( { error : "INTERNAL_ERROR" } ) ) ;
795+ }
0 commit comments