diff --git a/lib/utilities/collectResponseHeaders.js b/lib/utilities/collectResponseHeaders.js index 8480cf8d9f..99f3b6bba3 100644 --- a/lib/utilities/collectResponseHeaders.js +++ b/lib/utilities/collectResponseHeaders.js @@ -91,8 +91,12 @@ function collectResponseHeaders(objectMD, corsHeaders, versioningCfg, returnTagC } if (objectMD.replicationInfo) { const { isReplica, status } = objectMD.replicationInfo; - if (isReplica || status) { - responseMetaHeaders['x-amz-replication-status'] = isReplica ? 'REPLICA' : status; + if (isReplica) { + const hasReplicated = !status || status === 'COMPLETED'; + responseMetaHeaders['x-amz-replication-status'] = hasReplicated ? 'REPLICA' : status; + responseMetaHeaders['x-amz-meta-scal-replica'] = 'true'; + } else if (status) { + responseMetaHeaders['x-amz-replication-status'] = status; } } if (Array.isArray(objectMD?.replicationInfo?.backends)) { diff --git a/tests/unit/utils/collectResponseHeaders.js b/tests/unit/utils/collectResponseHeaders.js index de17b64321..5dd59103ba 100644 --- a/tests/unit/utils/collectResponseHeaders.js +++ b/tests/unit/utils/collectResponseHeaders.js @@ -8,12 +8,46 @@ describe('Middleware: Collect Response Headers', () => { assert.deepStrictEqual(headers['x-amz-replication-status'], 'REPLICA'); }); - it('should set REPLICA header from isReplica even when status is PENDING', () => { + it('should mark a replica with x-amz-meta-scal-replica', () => { const objectMD = { - replicationInfo: { status: 'PENDING', isReplica: true }, + replicationInfo: { status: 'REPLICA', isReplica: true }, }; const headers = collectResponseHeaders(objectMD); assert.deepStrictEqual(headers['x-amz-replication-status'], 'REPLICA'); + assert.deepStrictEqual(headers['x-amz-meta-scal-replica'], 'true'); + }); + + it('should default to REPLICA when isReplica is true and status is absent', () => { + const objectMD = { replicationInfo: { isReplica: true } }; + const headers = collectResponseHeaders(objectMD); + assert.deepStrictEqual(headers['x-amz-replication-status'], 'REPLICA'); + assert.deepStrictEqual(headers['x-amz-meta-scal-replica'], 'true'); + }); + + ['PENDING', 'PROCESSING', 'FAILED'].forEach(status => { + it(`should expose onward status ${status} on a cascaded replica`, () => { + const objectMD = { replicationInfo: { status, isReplica: true } }; + const headers = collectResponseHeaders(objectMD); + assert.deepStrictEqual(headers['x-amz-replication-status'], status); + assert.deepStrictEqual(headers['x-amz-meta-scal-replica'], 'true'); + }); + }); + + it('should report REPLICA once onward replication is COMPLETED', () => { + const objectMD = { + replicationInfo: { status: 'COMPLETED', isReplica: true }, + }; + const headers = collectResponseHeaders(objectMD); + assert.deepStrictEqual(headers['x-amz-replication-status'], 'REPLICA'); + assert.deepStrictEqual(headers['x-amz-meta-scal-replica'], 'true'); + }); + + it('should not mark x-amz-meta-scal-replica when not a replica', () => { + const objectMD = { + replicationInfo: { status: 'PENDING', isReplica: false }, + }; + const headers = collectResponseHeaders(objectMD); + assert.deepStrictEqual(headers['x-amz-meta-scal-replica'], undefined); }); it('should use replicationInfo.status when isReplica is false', () => {