1- const { waterfall } = require ( 'async ' ) ;
1+ const { promisify } = require ( 'util ' ) ;
22const xml2js = require ( 'xml2js' ) ;
33const { errors } = require ( 'arsenal' ) ;
44const { standardMetadataValidateBucketAndObj } = require ( '../metadata/metadataUtils' ) ;
@@ -7,9 +7,31 @@ const parseAttributesHeaders = require('./apiUtils/object/parseAttributesHeader'
77const { decodeVersionId, getVersionIdResHeader } = require ( './apiUtils/object/versioning' ) ;
88const { checkExpectedBucketOwner } = require ( './apiUtils/authorization/bucketOwner' ) ;
99const { pushMetric } = require ( '../utapi/utilities' ) ;
10+ const { getPartCountFromMd5 } = require ( './apiUtils/object/partInfo' ) ;
1011
1112const OBJECT_GET_ATTRIBUTES = 'objectGetAttributes' ;
1213
14+ const checkExpectedBucketOwnerPromise = promisify ( checkExpectedBucketOwner ) ;
15+
16+ /**
17+ * validateBucketAndObjPromise - Promisified wrapper for standardMetadataValidateBucketAndObj
18+ * @param {object } params - validation parameters
19+ * @param {boolean } actionImplicitDenies - whether action has implicit denies
20+ * @param {object } log - Werelogs logger
21+ * @returns {Promise<{bucket: BucketInfo, objMD: object}> } - bucket and object metadata
22+ * @throws {Error } - rejects with error from standardMetadataValidateBucketAndObj
23+ */
24+ function validateBucketAndObjPromise ( params , actionImplicitDenies , log ) {
25+ return new Promise ( ( resolve , reject ) => {
26+ standardMetadataValidateBucketAndObj ( params , actionImplicitDenies , log , ( err , bucket , objMD ) => {
27+ if ( err ) {
28+ return reject ( err ) ;
29+ }
30+ return resolve ( { bucket, objMD } ) ;
31+ } ) ;
32+ } ) ;
33+ }
34+
1335/**
1436 * buildXmlResponse - Build XML response for GetObjectAttributes
1537 * @param {object } objMD - object metadata
@@ -23,12 +45,16 @@ function buildXmlResponse(objMD, attributes) {
2345 attrResp . ETag = objMD [ 'content-md5' ] ;
2446 }
2547
48+ // NOTE: Checksum is not implemented
2649 if ( attributes . includes ( 'Checksum' ) ) {
2750 attrResp . Checksum = { } ;
2851 }
2952
3053 if ( attributes . includes ( 'ObjectParts' ) ) {
31- attrResp . ObjectParts = { } ;
54+ const partCount = getPartCountFromMd5 ( objMD ) ;
55+ if ( partCount ) {
56+ attrResp . ObjectParts = { PartsCount : partCount } ;
57+ }
3258 }
3359
3460 if ( attributes . includes ( 'StorageClass' ) ) {
@@ -51,19 +77,17 @@ function buildXmlResponse(objMD, attributes) {
5177 * @param {function } callback - callback to server
5278 * @return {undefined }
5379 */
54- function objectGetAttributes ( authInfo , request , log , callback ) {
55- log . debug ( 'processing request' , { method : OBJECT_GET_ATTRIBUTES } ) ;
80+ async function objectGetAttributes ( authInfo , request , log , callback ) {
81+ log . trace ( 'processing request' , { method : OBJECT_GET_ATTRIBUTES } ) ;
5682 const { bucketName, objectKey, headers, actionImplicitDenies } = request ;
5783
58- const decodedVersionId = decodeVersionId ( request . query ) ;
59- if ( decodedVersionId instanceof Error ) {
60- log . trace ( 'invalid versionId query' , {
61- versionId : request . query . versionId ,
62- error : decodedVersionId ,
63- } ) ;
64- return callback ( decodedVersionId ) ;
84+ let responseHeaders = { } ;
85+
86+ const versionId = decodeVersionId ( request . query ) ;
87+ if ( versionId instanceof Error ) {
88+ log . debug ( 'invalid versionId query' , { versionId : request . query . versionId , error : versionId } ) ;
89+ throw versionId ;
6590 }
66- const versionId = decodedVersionId ;
6791
6892 const metadataValParams = {
6993 authInfo,
@@ -75,72 +99,48 @@ function objectGetAttributes(authInfo, request, log, callback) {
7599 request,
76100 } ;
77101
78- let bucket = null ;
79- let objMD = null ;
80- let responseHeaders = null ;
81- let xml = null ;
82-
83- return waterfall (
84- [
85- next =>
86- standardMetadataValidateBucketAndObj ( metadataValParams , actionImplicitDenies , log , ( err , b , o ) => {
87- bucket = b ;
88- objMD = o ;
89- return next ( err ) ;
90- } ) ,
91- next => checkExpectedBucketOwner ( headers , bucket , log , next ) ,
92- next => {
93- responseHeaders = collectCorsHeaders ( headers . origin , request . method , bucket ) ;
94- if ( objMD ) {
95- responseHeaders [ 'x-amz-version-id' ] = getVersionIdResHeader ( bucket . getVersioningConfiguration ( ) , objMD ) ;
96- responseHeaders [ 'Last-Modified' ] = objMD [ 'last-modified' ] && new Date ( objMD [ 'last-modified' ] ) . toUTCString ( ) ;
97- }
98-
99- return next ( ) ;
100- } ,
101- next => {
102- if ( ! objMD ) {
103- const e = versionId ? errors . NoSuchVersion : errors . NoSuchKey ;
104- return next ( e ) ;
105- }
106-
107- if ( objMD . isDeleteMarker ) {
108- responseHeaders [ 'x-amz-delete-marker' ] = true ;
109- return next ( errors . MethodNotAllowed ) ;
110- }
111-
112- const attributes = parseAttributesHeaders ( headers ) ;
113- if ( attributes instanceof Error ) {
114- log . trace ( 'invalid x-amz-object-attributes header' , {
115- headers,
116- error : attributes ,
117- } ) ;
118- return next ( attributes ) ;
119- }
120-
121- xml = buildXmlResponse ( objMD , attributes ) ;
122- return next ( ) ;
123- } ,
124- ] ,
125- err => {
126- if ( err ) {
127- log . debug ( 'error processing request' , {
128- error : err ,
129- method : OBJECT_GET_ATTRIBUTES ,
130- } ) ;
131- return callback ( err , '' , responseHeaders ) ;
132- }
102+ try {
103+ const { bucket, objMD } = await validateBucketAndObjPromise ( metadataValParams , actionImplicitDenies , log ) ;
104+ await checkExpectedBucketOwnerPromise ( headers , bucket , log ) ;
105+
106+ responseHeaders = collectCorsHeaders ( headers . origin , request . method , bucket ) ;
107+ if ( objMD ) {
108+ responseHeaders [ 'x-amz-version-id' ] = getVersionIdResHeader ( bucket . getVersioningConfiguration ( ) , objMD ) ;
109+ responseHeaders [ 'Last-Modified' ] = objMD [ 'last-modified' ] && new Date ( objMD [ 'last-modified' ] ) . toUTCString ( ) ;
110+ }
111+
112+ if ( ! objMD ) {
113+ const err = versionId ? errors . NoSuchVersion : errors . NoSuchKey ;
114+ log . debug ( 'object not found' , { bucket : bucketName , key : objectKey , versionId } ) ;
115+ throw err ;
116+ }
117+
118+ if ( objMD . isDeleteMarker ) {
119+ log . debug ( 'attempt to get attributes of a delete marker' , { bucket : bucketName , key : objectKey , versionId } ) ;
120+ responseHeaders [ 'x-amz-delete-marker' ] = true ;
121+ throw errors . MethodNotAllowed ;
122+ }
123+
124+ const attributes = parseAttributesHeaders ( headers ) ;
125+
126+ pushMetric ( OBJECT_GET_ATTRIBUTES , log , {
127+ authInfo,
128+ bucket : bucketName ,
129+ keys : [ objectKey ] ,
130+ versionId : objMD ?. versionId ,
131+ location : objMD ?. dataStoreName ,
132+ } ) ;
133+
134+ const xml = buildXmlResponse ( objMD , attributes ) ;
135+ return callback ( null , xml , responseHeaders ) ;
136+ } catch ( err ) {
137+ log . debug ( 'error processing request' , {
138+ error : err ,
139+ method : OBJECT_GET_ATTRIBUTES ,
140+ } ) ;
133141
134- pushMetric ( OBJECT_GET_ATTRIBUTES , log , {
135- authInfo,
136- bucket : bucketName ,
137- keys : [ objectKey ] ,
138- versionId : objMD ?. versionId ,
139- location : objMD ?. dataStoreName ,
140- } ) ;
141- return callback ( err , xml , responseHeaders ) ;
142- } ,
143- ) ;
142+ return callback ( err , null , responseHeaders ) ;
143+ }
144144}
145145
146146module . exports = objectGetAttributes ;
0 commit comments