@@ -12,6 +12,7 @@ const backbeatProxy = httpProxy.createProxyServer({
1212} ) ;
1313const { auth, errors, errorInstances, s3middleware, s3routes, models, storage, versioning } = require ( 'arsenal' ) ;
1414const { decode, encode } = versioning . VersionID ;
15+ const { ExternalNullVersionId } = versioning . VersioningConstants ;
1516const {
1617 VersionIdCollisionException,
1718 StaleMicroVersionIdException,
@@ -434,35 +435,38 @@ function putData(request, response, bucketInfo, objMd, log, callback) {
434435
435436 const incomingVersionIdEncoded = request . headers [ 'x-scal-version-id' ] ;
436437 if ( incomingVersionIdEncoded !== undefined ) {
437- const incomingVersionIdDecoded =
438- incomingVersionIdEncoded !== 'null' ? decode ( incomingVersionIdEncoded ) : 'null' ;
439- if ( incomingVersionIdDecoded instanceof Error ) {
440- log . error ( 'crr putData: failed to decode x-scal-version-id header' , {
441- method : 'putData' ,
442- error : incomingVersionIdDecoded . message ,
443- } ) ;
444- return callback (
445- errorInstances . BadRequest . customizeDescription ( 'bad request: invalid x-scal-version-id header' ) ,
446- ) ;
447- }
448- if ( objMd && objMd . versionId === incomingVersionIdDecoded ) {
449- // Data already at destination for this version; return 409 with the existing
450- // microVersionId so backbeat can decide if putMetadata is still needed.
451- log . debug ( 'crr putData: version already at destination' , {
452- method : 'putData' ,
453- bucketName : request . bucketName ,
454- objectKey : request . objectKey ,
455- hasMicroVersionId : ! ! objMd . microVersionId ,
456- } ) ;
457- request . resume ( ) ;
458- return _respondWithHeaderCrrConflict (
459- response ,
460- log ,
461- callback ,
462- VersionIdCollisionException . name ,
463- 'version id already at destination' ,
464- objMd . microVersionId ,
465- ) ;
438+ // ExternalNullVersionId means a null version, which does not need decoding
439+ if ( incomingVersionIdEncoded !== ExternalNullVersionId ) {
440+ const decoded = decode ( incomingVersionIdEncoded ) ;
441+ if ( decoded instanceof Error ) {
442+ log . error ( 'crr putData: failed to decode x-scal-version-id header' , {
443+ method : 'putData' ,
444+ error : decoded . message ,
445+ } ) ;
446+ return callback (
447+ errorInstances . BadRequest . customizeDescription ( 'bad request: invalid x-scal-version-id header' ) ,
448+ ) ;
449+ }
450+ if ( objMd ) {
451+ // objMd is the specific version requested by the header x-scal-version-id.
452+ // Its existence means the data is already at the destination. Return 409 with the
453+ // existing microVersionId so backbeat can decide if putMetadata is still needed.
454+ log . debug ( 'crr putData: version already at destination' , {
455+ method : 'putData' ,
456+ bucketName : request . bucketName ,
457+ objectKey : request . objectKey ,
458+ hasMicroVersionId : ! ! objMd . microVersionId ,
459+ } ) ;
460+ request . resume ( ) ;
461+ return _respondWithHeaderCrrConflict (
462+ response ,
463+ log ,
464+ callback ,
465+ VersionIdCollisionException . name ,
466+ 'version id already at destination' ,
467+ objMd . microVersionId ,
468+ ) ;
469+ }
466470 }
467471 }
468472
@@ -2039,15 +2043,31 @@ function routeBackbeat(clientIP, request, response, log) {
20392043 if ( ! isObjectRequest ) {
20402044 return routeNonObjectRequest ( request , response , userInfo , log , next ) ;
20412045 }
2042- const decodedVidResult = decodeVersionId ( request . query ) ;
2043- if ( decodedVidResult instanceof Error ) {
2044- log . trace ( 'invalid versionId query' , {
2045- versionId : request . query . versionId ,
2046- error : decodedVidResult ,
2047- } ) ;
2048- return next ( errors . InvalidArgument ) ;
2046+ const versionIdHeader = request . headers [ 'x-scal-version-id' ] ;
2047+ let versionId ;
2048+ if ( versionIdHeader !== undefined ) {
2049+ if ( versionIdHeader !== ExternalNullVersionId ) {
2050+ const decoded = decode ( versionIdHeader ) ;
2051+ if ( decoded instanceof Error ) {
2052+ return next (
2053+ errorInstances . BadRequest . customizeDescription (
2054+ 'bad request: invalid x-scal-version-id header' ,
2055+ ) ,
2056+ ) ;
2057+ }
2058+ versionId = decoded ;
2059+ }
2060+ } else {
2061+ const decodedVidResult = decodeVersionId ( request . query ) ;
2062+ if ( decodedVidResult instanceof Error ) {
2063+ log . trace ( 'invalid versionId query' , {
2064+ versionId : request . query . versionId ,
2065+ error : decodedVidResult ,
2066+ } ) ;
2067+ return next ( errors . InvalidArgument ) ;
2068+ }
2069+ versionId = decodedVidResult ;
20492070 }
2050- const versionId = decodedVidResult ;
20512071 if ( useMultipleBackend ) {
20522072 if ( request . resourceType === 'multiplebackendmetadata' ) {
20532073 return backbeatRoutes [ request . method ] [ request . resourceType ] ( request , response , log , next ) ;
0 commit comments