diff --git a/index.js b/index.js index 6672a8967..c3dc4ee7f 100644 --- a/index.js +++ b/index.js @@ -55,6 +55,9 @@ module.exports = { convertToXml: require('./lib/s3middleware/convertToXml'), escapeForXml: require('./lib/s3middleware/escapeForXml'), tagging: require('./lib/s3middleware/tagging'), + checkDateModifiedHeaders: + require('./lib/s3middleware/validateConditionalHeaders') + .checkDateModifiedHeaders, validateConditionalHeaders: require('./lib/s3middleware/validateConditionalHeaders') .validateConditionalHeaders, diff --git a/lib/s3middleware/validateConditionalHeaders.js b/lib/s3middleware/validateConditionalHeaders.js index 38cfb55a5..4a0eba2b0 100644 --- a/lib/s3middleware/validateConditionalHeaders.js +++ b/lib/s3middleware/validateConditionalHeaders.js @@ -68,6 +68,31 @@ function _checkUnmodifiedSince(ifUnmodifiedSinceTime, lastModified) { return res; } +/** + * checks 'if-modified-since' and 'if-unmodified-since' headers if included in + * request against last-modified date of object + * @param {object} headers - headers from request object + * @param {string} lastModified - last modified date of object + * @return {object} contains modifiedSince and unmodifiedSince res objects + */ +function checkDateModifiedHeaders(headers, lastModified) { + let lastModifiedDate = new Date(lastModified); + lastModifiedDate.setMilliseconds(0); + lastModifiedDate = lastModifiedDate.getTime(); + + const ifModifiedSinceHeader = headers['if-modified-since'] || + headers['x-amz-copy-source-if-modified-since']; + const ifUnmodifiedSinceHeader = headers['if-unmodified-since'] || + headers['x-amz-copy-source-if-unmodified-since']; + + const modifiedSinceRes = _checkModifiedSince(ifModifiedSinceHeader, + lastModifiedDate); + const unmodifiedSinceRes = _checkUnmodifiedSince(ifUnmodifiedSinceHeader, + lastModifiedDate); + + return { modifiedSinceRes, unmodifiedSinceRes }; +} + /** * validateConditionalHeaders - validates 'if-modified-since', * 'if-unmodified-since', 'if-match' or 'if-none-match' headers if included in @@ -79,23 +104,14 @@ function _checkUnmodifiedSince(ifUnmodifiedSinceTime, lastModified) { * empty object if no error */ function validateConditionalHeaders(headers, lastModified, contentMD5) { - let lastModifiedDate = new Date(lastModified); - lastModifiedDate.setMilliseconds(0); - lastModifiedDate = lastModifiedDate.getTime(); const ifMatchHeader = headers['if-match'] || headers['x-amz-copy-source-if-match']; const ifNoneMatchHeader = headers['if-none-match'] || headers['x-amz-copy-source-if-none-match']; - const ifModifiedSinceHeader = headers['if-modified-since'] || - headers['x-amz-copy-source-if-modified-since']; - const ifUnmodifiedSinceHeader = headers['if-unmodified-since'] || - headers['x-amz-copy-source-if-unmodified-since']; const etagMatchRes = _checkEtagMatch(ifMatchHeader, contentMD5); const etagNoneMatchRes = _checkEtagNoneMatch(ifNoneMatchHeader, contentMD5); - const modifiedSinceRes = _checkModifiedSince(ifModifiedSinceHeader, - lastModifiedDate); - const unmodifiedSinceRes = _checkUnmodifiedSince(ifUnmodifiedSinceHeader, - lastModifiedDate); + const { modifiedSinceRes, unmodifiedSinceRes } = + checkDateModifiedHeaders(headers, lastModified); // If-Unmodified-Since condition evaluates to false and If-Match // is not present, then return the error. Otherwise, If-Unmodified-Since is // silent when If-Match match, and when If-Match does not match, it's the @@ -120,5 +136,6 @@ module.exports = { _checkEtagNoneMatch, _checkModifiedSince, _checkUnmodifiedSince, + checkDateModifiedHeaders, validateConditionalHeaders, }; diff --git a/tests/unit/s3middleware/validateConditionalHeaders.js b/tests/unit/s3middleware/validateConditionalHeaders.js index e38e762f1..8ed21dd87 100644 --- a/tests/unit/s3middleware/validateConditionalHeaders.js +++ b/tests/unit/s3middleware/validateConditionalHeaders.js @@ -7,6 +7,7 @@ const { _checkEtagNoneMatch, _checkModifiedSince, _checkUnmodifiedSince, + checkDateModifiedHeaders, validateConditionalHeaders, } = require('../../../lib/s3middleware/validateConditionalHeaders'); @@ -172,6 +173,59 @@ describe('validateConditionalHeaders util function ::', () => { }); }); +describe('checkDateModifiedHeaders util function: ', () => { + const expectedSuccess = { + present: true, + error: null, + }; + + const expectedAbsense = { + present: false, + error: null, + }; + + it('should return NotModified error for \'if-modified-since\' header', + () => { + const header = {}; + header['if-modified-since'] = afterLastModified; + const { modifiedSinceRes, unmodifiedSinceRes } = + checkDateModifiedHeaders(header, lastModified); + assert.deepStrictEqual(modifiedSinceRes.error, errors.NotModified); + assert.deepStrictEqual(unmodifiedSinceRes, expectedAbsense); + }); + + it('should return PreconditionFailed error for \'if-unmodified-since\' ' + + 'header', () => { + const header = {}; + header['if-unmodified-since'] = beforeLastModified; + const { modifiedSinceRes, unmodifiedSinceRes } = + checkDateModifiedHeaders(header, lastModified); + assert.deepStrictEqual(unmodifiedSinceRes.error, + errors.PreconditionFailed); + assert.deepStrictEqual(modifiedSinceRes, expectedAbsense); + }); + + it('should succeed if \'if-modified-since\' header value is earlier ' + + 'than last modified', () => { + const header = {}; + header['if-modified-since'] = beforeLastModified; + const { modifiedSinceRes, unmodifiedSinceRes } = + checkDateModifiedHeaders(header, lastModified); + assert.deepStrictEqual(modifiedSinceRes, expectedSuccess); + assert.deepStrictEqual(unmodifiedSinceRes, expectedAbsense); + }); + + it('should succeed if \'if-unmodified-since\' header value is later ' + + 'than last modified', () => { + const header = {}; + header['if-unmodified-since'] = afterLastModified; + const { modifiedSinceRes, unmodifiedSinceRes } = + checkDateModifiedHeaders(header, lastModified); + assert.deepStrictEqual(unmodifiedSinceRes, expectedSuccess); + assert.deepStrictEqual(modifiedSinceRes, expectedAbsense); + }); +}); + describe('_checkEtagMatch function :', () => { const expectedSuccess = { present: true,