@@ -8,14 +8,9 @@ const escapeForXml = s3middleware.escapeForXml;
88const { pushMetric } = require ( '../utapi/utilities' ) ;
99const versionIdUtils = versioning . VersionID ;
1010const monitoring = require ( '../utilities/monitoringHandler' ) ;
11- const { generateToken, decryptToken }
12- = require ( '../api/apiUtils/object/continueToken' ) ;
11+ const { generateToken, decryptToken } = require ( '../api/apiUtils/object/continueToken' ) ;
1312
14- // do not url encode the continuation tokens
15- const skipUrlEncoding = new Set ( [
16- 'ContinuationToken' ,
17- 'NextContinuationToken' ,
18- ] ) ;
13+ const xmlParamsToSkipUrlEncoding = new Set ( [ 'ContinuationToken' , 'NextContinuationToken' , ] ) ;
1914
2015/* Sample XML response for GET bucket objects V2:
2116<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
@@ -122,17 +117,16 @@ function processVersions(bucketName, listParams, list) {
122117 { tag : 'IsTruncated' , value : isTruncated } ,
123118 ] ;
124119
125- const escapeXmlFn = listParams . encoding === 'url' ?
126- querystring . escape : escapeForXml ;
120+ const escapeXmlFn = listParams . encoding === 'url' ? querystring . escape : escapeForXml ;
127121 xmlParams . forEach ( p => {
128122 if ( p . value ) {
129123 const val = p . tag !== 'NextVersionIdMarker' || p . value === 'null' ?
130- p . value : versionIdUtils . encode ( p . value ) ;
124+ p . value :
125+ versionIdUtils . encode ( p . value ) ;
131126 xml . push ( `<${ p . tag } >${ escapeXmlFn ( val ) } </${ p . tag } >` ) ;
132127 }
133128 } ) ;
134- let lastKey = listParams . keyMarker ?
135- escapeXmlFn ( listParams . keyMarker ) : undefined ;
129+ let lastKey = listParams . keyMarker ? escapeXmlFn ( listParams . keyMarker ) : undefined ;
136130 list . Versions . forEach ( item => {
137131 const v = item . value ;
138132 const objectKey = escapeXmlFn ( item . key ) ;
@@ -143,7 +137,8 @@ function processVersions(bucketName, listParams, list) {
143137 `<Key>${ objectKey } </Key>` ,
144138 '<VersionId>' ,
145139 ( v . IsNull || v . VersionId === undefined ) ?
146- 'null' : versionIdUtils . encode ( v . VersionId ) ,
140+ 'null'
141+ : versionIdUtils . encode ( v . VersionId ) ,
147142 '</VersionId>' ,
148143 `<IsLatest>${ isLatest } </IsLatest>` ,
149144 `<LastModified>${ v . LastModified } </LastModified>` ,
@@ -182,31 +177,19 @@ function processMasterVersions(bucketName, listParams, list) {
182177 ] ;
183178
184179 if ( listParams . v2 ) {
185- xmlParams . push (
186- { tag : 'StartAfter' , value : listParams . startAfter || '' } ) ;
187- xmlParams . push (
188- { tag : 'FetchOwner' , value : `${ listParams . fetchOwner } ` } ) ;
189- xmlParams . push ( {
190- tag : 'ContinuationToken' ,
191- value : generateToken ( listParams . continuationToken ) || '' ,
192- } ) ;
193- xmlParams . push ( {
194- tag : 'NextContinuationToken' ,
195- value : generateToken ( list . NextContinuationToken ) ,
196- } ) ;
197- xmlParams . push ( {
198- tag : 'KeyCount' ,
199- value : list . Contents ? list . Contents . length : 0 ,
200- } ) ;
180+ xmlParams . push ( { tag : 'StartAfter' , value : listParams . startAfter || '' } ) ;
181+ xmlParams . push ( { tag : 'FetchOwner' , value : `${ listParams . fetchOwner } ` } ) ;
182+ xmlParams . push ( { tag : 'ContinuationToken' , value : generateToken ( listParams . continuationToken ) || '' , } ) ;
183+ xmlParams . push ( { tag : 'NextContinuationToken' , value : generateToken ( list . NextContinuationToken ) , } ) ;
184+ xmlParams . push ( { tag : 'KeyCount' , value : list . Contents ? list . Contents . length : 0 , } ) ;
201185 } else {
202186 xmlParams . push ( { tag : 'Marker' , value : listParams . marker || '' } ) ;
203187 xmlParams . push ( { tag : 'NextMarker' , value : list . NextMarker } ) ;
204188 }
205189
206- const escapeXmlFn = listParams . encoding === 'url' ?
207- querystring . escape : escapeForXml ;
190+ const escapeXmlFn = listParams . encoding === 'url' ? querystring . escape : escapeForXml ;
208191 xmlParams . forEach ( p => {
209- if ( p . value && skipUrlEncoding . has ( p . tag ) ) {
192+ if ( p . value && xmlParamsToSkipUrlEncoding . has ( p . tag ) ) {
210193 xml . push ( `<${ p . tag } >${ p . value } </${ p . tag } >` ) ;
211194 } else if ( p . value || p . tag === 'KeyCount' || p . tag === 'MaxKeys' ) {
212195 xml . push ( `<${ p . tag } >${ escapeXmlFn ( p . value ) } </${ p . tag } >` ) ;
@@ -246,15 +229,14 @@ function processMasterVersions(bucketName, listParams, list) {
246229 ) ;
247230 } ) ;
248231 list . CommonPrefixes . forEach ( item => {
249- const val = escapeXmlFn ( item ) ;
250- xml . push ( `<CommonPrefixes><Prefix>${ val } </Prefix></CommonPrefixes>` ) ;
232+ xml . push ( `<CommonPrefixes><Prefix>${ escapeXmlFn ( item ) } </Prefix></CommonPrefixes>` ) ;
251233 } ) ;
252234 xml . push ( '</ListBucketResult>' ) ;
235+
253236 return xml . join ( '' ) ;
254237}
255238
256- function handleResult ( listParams , requestMaxKeys , encoding , authInfo ,
257- bucketName , list , corsHeaders , log , callback ) {
239+ function handleResult ( listParams , requestMaxKeys , encoding , authInfo , bucketName , list , log ) {
258240 // eslint-disable-next-line no-param-reassign
259241 listParams . maxKeys = requestMaxKeys ;
260242 // eslint-disable-next-line no-param-reassign
@@ -267,9 +249,24 @@ function handleResult(listParams, requestMaxKeys, encoding, authInfo,
267249 }
268250 pushMetric ( 'listBucket' , log , { authInfo, bucket : bucketName } ) ;
269251 monitoring . promMetrics ( 'GET' , bucketName , '200' , 'listBucket' ) ;
270- return callback ( null , res , corsHeaders ) ;
252+ return res ;
271253}
272254
255+ const validateBucket = ( params , denials , log ) => new Promise ( resolve => {
256+ standardMetadataValidateBucket ( params , denials , log , ( error , bucket ) => {
257+ resolve ( { error, bucket } ) ;
258+ } ) ;
259+ } ) ;
260+
261+ const getObjectListing = ( bucketName , listParams , log ) => new Promise ( ( resolve , reject ) => {
262+ services . getObjectListing ( bucketName , listParams , log , ( err , list ) => {
263+ if ( err ) {
264+ return reject ( err ) ;
265+ }
266+ return resolve ( list ) ;
267+ } ) ;
268+ } ) ;
269+
273270/**
274271 * bucketGet - Return list of objects in bucket, supports v1 & v2
275272 * @param {AuthInfo } authInfo - Instance of AuthInfo class with
@@ -280,92 +277,86 @@ function handleResult(listParams, requestMaxKeys, encoding, authInfo,
280277 * with either error code or xml response body
281278 * @return {undefined }
282279 */
283- function bucketGet ( authInfo , request , log , callback ) {
284- const params = request . query ;
285- const bucketName = request . bucketName ;
286- const v2 = params [ 'list-type' ] ;
287- if ( v2 !== undefined && Number . parseInt ( v2 , 10 ) !== 2 ) {
288- return callback ( errorInstances . InvalidArgument . customizeDescription ( 'Invalid ' +
289- 'List Type specified in Request' ) ) ;
290- }
291- if ( v2 ) {
292- log . addDefaultFields ( {
293- action : 'ListObjectsV2' ,
294- } ) ;
295- if ( request . serverAccessLog ) {
296- // eslint-disable-next-line no-param-reassign
297- request . serverAccessLog . analyticsAction = 'ListObjectsV2' ;
280+ async function bucketGet ( authInfo , request , log , callback ) {
281+ try {
282+ const params = request . query ;
283+ const bucketName = request . bucketName ;
284+ const v2 = params [ 'list-type' ] ;
285+
286+ if ( v2 !== undefined && Number . parseInt ( v2 , 10 ) !== 2 ) {
287+ return callback ( errorInstances . InvalidArgument . customizeDescription (
288+ 'Invalid List Type specified in Request'
289+ ) ) ;
298290 }
299- } else if ( params . versions !== undefined ) {
300- log . addDefaultFields ( {
301- action : 'ListObjectVersions' ,
302- } ) ;
303- if ( request . serverAccessLog ) {
304- // eslint-disable-next-line no-param-reassign
305- request . serverAccessLog . analyticsAction = 'ListObjectVersions' ;
291+
292+ if ( v2 ) {
293+ log . addDefaultFields ( { action : 'ListObjectsV2' , } ) ;
294+ if ( request . serverAccessLog ) {
295+ // eslint-disable-next-line no-param-reassign
296+ request . serverAccessLog . analyticsAction = 'ListObjectsV2' ;
297+ }
298+ } else if ( params . versions !== undefined ) {
299+ log . addDefaultFields ( { action : 'ListObjectVersions' , } ) ;
300+ if ( request . serverAccessLog ) {
301+ // eslint-disable-next-line no-param-reassign
302+ request . serverAccessLog . analyticsAction = 'ListObjectVersions' ;
303+ }
306304 }
307- }
308- log . debug ( 'processing request' , { method : 'bucketGet' } ) ;
309- const encoding = params [ 'encoding-type' ] ;
310- if ( encoding !== undefined && encoding !== 'url' ) {
311- monitoring . promMetrics (
312- 'GET' , bucketName , 400 , 'listBucket' ) ;
313- return callback ( errorInstances . InvalidArgument . customizeDescription ( 'Invalid ' +
314- 'Encoding Method specified in Request' ) ) ;
315- }
316- const requestMaxKeys = params [ 'max-keys' ] ?
317- Number . parseInt ( params [ 'max-keys' ] , 10 ) : 1000 ;
318- if ( Number . isNaN ( requestMaxKeys ) || requestMaxKeys < 0 ) {
319- monitoring . promMetrics (
320- 'GET' , bucketName , 400 , 'listBucket' ) ;
321- return callback ( errors . InvalidArgument ) ;
322- }
323- // AWS only returns 1000 keys even if max keys are greater.
324- // Max keys stated in response xml can be greater than actual
325- // keys returned.
326- const actualMaxKeys = Math . min ( constants . listingHardLimit , requestMaxKeys ) ;
305+ log . debug ( 'processing request' , { method : 'bucketGet' } ) ;
306+ const encoding = params [ 'encoding-type' ] ;
307+ if ( encoding !== undefined && encoding !== 'url' ) {
308+ monitoring . promMetrics ( 'GET' , bucketName , 400 , 'listBucket' ) ;
309+ return callback ( errorInstances . InvalidArgument . customizeDescription (
310+ 'Invalid Encoding Method specified in Request'
311+ ) ) ;
312+ }
313+ const requestMaxKeys = params [ 'max-keys' ] ? Number . parseInt ( params [ 'max-keys' ] , 10 ) : 1000 ;
314+ if ( Number . isNaN ( requestMaxKeys ) || requestMaxKeys < 0 ) {
315+ monitoring . promMetrics ( 'GET' , bucketName , 400 , 'listBucket' ) ;
316+ return callback ( errors . InvalidArgument ) ;
317+ }
318+ const actualMaxKeys = Math . min ( constants . listingHardLimit , requestMaxKeys ) ;
327319
328- const metadataValParams = {
329- authInfo,
330- bucketName,
331- requestType : request . apiMethods || 'bucketGet' ,
332- request,
333- } ;
334- const listParams = {
335- listingType : 'DelimiterMaster' ,
336- maxKeys : actualMaxKeys ,
337- prefix : params . prefix ,
338- } ;
320+ const metadataValParams = {
321+ authInfo,
322+ bucketName,
323+ requestType : request . apiMethods || 'bucketGet' ,
324+ request,
325+ } ;
326+ const listParams = {
327+ listingType : 'DelimiterMaster' ,
328+ maxKeys : actualMaxKeys ,
329+ prefix : params . prefix ,
330+ } ;
339331
340- if ( params . delimiter ) {
341- listParams . delimiter = params . delimiter ;
342- }
332+ if ( params . delimiter ) {
333+ listParams . delimiter = params . delimiter ;
334+ }
343335
344- if ( v2 ) {
345- listParams . v2 = true ;
346- listParams . startAfter = params [ 'start-after' ] ;
347- listParams . continuationToken =
348- decryptToken ( params [ 'continuation-token' ] ) ;
349- listParams . fetchOwner = params [ 'fetch-owner' ] === 'true' ;
350- } else {
351- listParams . marker = params . marker ;
352- }
336+ if ( v2 ) {
337+ listParams . v2 = true ;
338+ listParams . startAfter = params [ 'start-after' ] ;
339+ listParams . continuationToken = decryptToken ( params [ 'continuation-token' ] ) ;
340+ listParams . fetchOwner = params [ 'fetch-owner' ] === 'true' ;
341+ } else {
342+ listParams . marker = params . marker ;
343+ }
353344
354- standardMetadataValidateBucket ( metadataValParams , request . actionImplicitDenies , log , ( err , bucket ) => {
355- const corsHeaders = collectCorsHeaders ( request . headers . origin ,
356- request . method , bucket ) ;
357- if ( err ) {
358- log . debug ( 'error processing request' , { error : err } ) ;
359- monitoring . promMetrics (
360- 'GET' , bucketName , err . code , 'listBucket' ) ;
361- return callback ( err , null , corsHeaders ) ;
345+ const { error, bucket } = await validateBucket ( metadataValParams , request . actionImplicitDenies , log ) ;
346+ const corsHeaders = collectCorsHeaders ( request . headers . origin , request . method , bucket ) ;
347+
348+ if ( error ) {
349+ log . debug ( 'error processing request' , { error } ) ;
350+ monitoring . promMetrics ( 'GET' , bucketName , error . code , 'listBucket' ) ;
351+ return callback ( error , null , corsHeaders ) ;
362352 }
363353 if ( params . versions !== undefined ) {
364354 listParams . listingType = 'DelimiterVersions' ;
365355 delete listParams . marker ;
366356 listParams . keyMarker = params [ 'key-marker' ] ;
367357 listParams . versionIdMarker = params [ 'version-id-marker' ] ?
368- versionIdUtils . decode ( params [ 'version-id-marker' ] ) : undefined ;
358+ versionIdUtils . decode ( params [ 'version-id-marker' ] ) :
359+ undefined ;
369360 }
370361 if ( ! requestMaxKeys ) {
371362 const emptyList = {
@@ -374,26 +365,30 @@ function bucketGet(authInfo, request, log, callback) {
374365 Versions : [ ] ,
375366 IsTruncated : false ,
376367 } ;
377- return handleResult ( listParams , requestMaxKeys , encoding , authInfo ,
378- bucketName , emptyList , corsHeaders , log , callback ) ;
368+ const res = handleResult ( listParams , requestMaxKeys , encoding , authInfo , bucketName , emptyList , log ) ;
369+ return callback ( null , res , corsHeaders ) ;
379370 }
380- return services . getObjectListing ( bucketName , listParams , log ,
381- ( err , list ) => {
382- if ( err ) {
383- log . debug ( 'error processing request' , { error : err } ) ;
384- monitoring . promMetrics (
385- 'GET' , bucketName , err . code , 'listBucket' ) ;
386- return callback ( err , null , corsHeaders ) ;
387- }
388- return handleResult ( listParams , requestMaxKeys , encoding , authInfo ,
389- bucketName , list , corsHeaders , log , callback ) ;
390- } ) ;
391- } ) ;
392- return undefined ;
371+
372+ let list ;
373+ try {
374+ list = await getObjectListing ( bucketName , listParams , log ) ;
375+ } catch ( err ) {
376+ log . debug ( 'error processing request' , { error : err } ) ;
377+ monitoring . promMetrics ( 'GET' , bucketName , err . code , 'listBucket' ) ;
378+ return callback ( err , null , corsHeaders ) ;
379+ }
380+
381+ const res = handleResult ( listParams , requestMaxKeys , encoding , authInfo , bucketName , list , log ) ;
382+ return callback ( null , res , corsHeaders ) ;
383+ } catch ( err ) {
384+ log . error ( 'unhandled error in bucketGet' , { error : err } ) ;
385+ return callback ( errors . InternalError ) ;
386+ }
393387}
394388
395389module . exports = {
396390 processVersions,
397391 processMasterVersions,
398392 bucketGet,
399393} ;
394+
0 commit comments