| /*! |
| * etag |
| * Copyright(c) 2014-2016 Douglas Christopher Wilson |
| * MIT Licensed |
| */ |
| |
| 'use strict' |
| |
| /** |
| * Module exports. |
| * @public |
| */ |
| |
| module.exports = etag |
| |
| /** |
| * Module dependencies. |
| * @private |
| */ |
| |
| var crypto = require('crypto') |
| var Stats = require('fs').Stats |
| |
| /** |
| * Module variables. |
| * @private |
| */ |
| |
| var toString = Object.prototype.toString |
| |
| /** |
| * Generate an entity tag. |
| * |
| * @param {Buffer|string} entity |
| * @return {string} |
| * @private |
| */ |
| |
| function entitytag (entity) { |
| if (entity.length === 0) { |
| // fast-path empty |
| return '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"' |
| } |
| |
| // compute hash of entity |
| var hash = crypto |
| .createHash('sha1') |
| .update(entity, 'utf8') |
| .digest('base64') |
| .substring(0, 27) |
| |
| // compute length of entity |
| var len = typeof entity === 'string' |
| ? Buffer.byteLength(entity, 'utf8') |
| : entity.length |
| |
| return '"' + len.toString(16) + '-' + hash + '"' |
| } |
| |
| /** |
| * Create a simple ETag. |
| * |
| * @param {string|Buffer|Stats} entity |
| * @param {object} [options] |
| * @param {boolean} [options.weak] |
| * @return {String} |
| * @public |
| */ |
| |
| function etag (entity, options) { |
| if (entity == null) { |
| throw new TypeError('argument entity is required') |
| } |
| |
| // support fs.Stats object |
| var isStats = isstats(entity) |
| var weak = options && typeof options.weak === 'boolean' |
| ? options.weak |
| : isStats |
| |
| // validate argument |
| if (!isStats && typeof entity !== 'string' && !Buffer.isBuffer(entity)) { |
| throw new TypeError('argument entity must be string, Buffer, or fs.Stats') |
| } |
| |
| // generate entity tag |
| var tag = isStats |
| ? stattag(entity) |
| : entitytag(entity) |
| |
| return weak |
| ? 'W/' + tag |
| : tag |
| } |
| |
| /** |
| * Determine if object is a Stats object. |
| * |
| * @param {object} obj |
| * @return {boolean} |
| * @api private |
| */ |
| |
| function isstats (obj) { |
| // genuine fs.Stats |
| if (typeof Stats === 'function' && obj instanceof Stats) { |
| return true |
| } |
| |
| // quack quack |
| return obj && typeof obj === 'object' && |
| 'ctime' in obj && toString.call(obj.ctime) === '[object Date]' && |
| 'mtime' in obj && toString.call(obj.mtime) === '[object Date]' && |
| 'ino' in obj && typeof obj.ino === 'number' && |
| 'size' in obj && typeof obj.size === 'number' |
| } |
| |
| /** |
| * Generate a tag for a stat. |
| * |
| * @param {object} stat |
| * @return {string} |
| * @private |
| */ |
| |
| function stattag (stat) { |
| var mtime = stat.mtime.getTime().toString(16) |
| var size = stat.size.toString(16) |
| |
| return '"' + size + '-' + mtime + '"' |
| } |