socketioMEANseed/server/node_modules/etag/index.js
server/node_modules/etag/index.jsRaw
1/*!
2 * etag
3 * Copyright(c) 2014 Douglas Christopher Wilson
4 * MIT Licensed
5 */
6
7/**
8 * Module exports.
9 */
10
11module.exports = etag
12
13/**
14 * Module dependencies.
15 */
16
17var crc = require('crc').crc32
18var crypto = require('crypto')
19var Stats = require('fs').Stats
20
21/**
22 * Module variables.
23 */
24
25var crc32threshold = 1000 // 1KB
26var NULL = new Buffer([0])
27var toString = Object.prototype.toString
28
29/**
30 * Create a simple ETag.
31 *
32 * @param {string|Buffer|Stats} entity
33 * @param {object} [options]
34 * @param {boolean} [options.weak]
35 * @return {String}
36 * @api public
37 */
38
39function etag(entity, options) {
40 if (entity == null) {
41 throw new TypeError('argument entity is required')
42 }
43
44 var isStats = isstats(entity)
45 var weak = options && typeof options.weak === 'boolean'
46 ? options.weak
47 : isStats
48
49 // support fs.Stats object
50 if (isStats) {
51 return stattag(entity, weak)
52 }
53
54 if (typeof entity !== 'string' && !Buffer.isBuffer(entity)) {
55 throw new TypeError('argument entity must be string, Buffer, or fs.Stats')
56 }
57
58 var hash = weak
59 ? weakhash(entity)
60 : stronghash(entity)
61
62 return weak
63 ? 'W/"' + hash + '"'
64 : '"' + hash + '"'
65}
66
67/**
68 * Determine if object is a Stats object.
69 *
70 * @param {object} obj
71 * @return {boolean}
72 * @api private
73 */
74
75function isstats(obj) {
76 // not even an object
77 if (obj === null || typeof obj !== 'object') {
78 return false
79 }
80
81 // genuine fs.Stats
82 if (obj instanceof Stats) {
83 return true
84 }
85
86 // quack quack
87 return 'atime' in obj && toString.call(obj.atime) === '[object Date]'
88 && 'ctime' in obj && toString.call(obj.ctime) === '[object Date]'
89 && 'mtime' in obj && toString.call(obj.mtime) === '[object Date]'
90 && 'ino' in obj && typeof obj.ino === 'number'
91 && 'size' in obj && typeof obj.size === 'number'
92}
93
94/**
95 * Generate a tag for a stat.
96 *
97 * @param {Buffer} entity
98 * @return {String}
99 * @api private
100 */
101
102function stattag(stat, weak) {
103 var mtime = stat.mtime.toISOString()
104 var size = stat.size.toString(16)
105
106 if (weak) {
107 return 'W/"' + size + '-' + crc(mtime) + '"'
108 }
109
110 var hash = crypto
111 .createHash('md5')
112 .update('file', 'utf8')
113 .update(NULL)
114 .update(size, 'utf8')
115 .update(NULL)
116 .update(mtime, 'utf8')
117 .digest('base64')
118
119 return '"' + hash + '"'
120}
121
122/**
123 * Generate a strong hash.
124 *
125 * @param {Buffer} entity
126 * @return {String}
127 * @api private
128 */
129
130function stronghash(entity) {
131 if (entity.length === 0) {
132 // fast-path empty
133 return '1B2M2Y8AsgTpgAmY7PhCfg=='
134 }
135
136 return crypto
137 .createHash('md5')
138 .update(entity, 'utf8')
139 .digest('base64')
140}
141
142/**
143 * Generate a weak hash.
144 *
145 * @param {Buffer} entity
146 * @return {String}
147 * @api private
148 */
149
150function weakhash(entity) {
151 if (entity.length === 0) {
152 // fast-path empty
153 return '0-0'
154 }
155
156 var len = typeof entity === 'string'
157 ? Buffer.byteLength(entity, 'utf8')
158 : entity.length
159
160 if (len <= crc32threshold) {
161 // crc32 plus length when it's fast
162 // crc(str) only accepts utf-8 encoding
163 return len.toString(16) + '-' + crc(entity).toString(16)
164 }
165
166 // use md4 for long strings
167 return crypto
168 .createHash('md4')
169 .update(entity, 'utf8')
170 .digest('base64')
171}