utility.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. /*!
  2. * utility - lib/utility.js
  3. *
  4. * Copyright(c) 2012 - 2013 fengmk2 <fengmk2@gmail.com>
  5. * MIT Licensed
  6. */
  7. "use strict";
  8. /**
  9. * Module dependencies.
  10. */
  11. var crypto = require('crypto');
  12. var address = require('address');
  13. /**
  14. * A empty function.
  15. *
  16. * @return {Function}
  17. * @public
  18. */
  19. exports.noop = function () {};
  20. function sortObject(o) {
  21. if (!o || Array.isArray(o) || typeof o !== 'object') {
  22. return o;
  23. }
  24. var keys = Object.keys(o);
  25. keys.sort();
  26. var values = [];
  27. for (var i = 0; i < keys.length; i++) {
  28. var k = keys[i];
  29. values.push([k, sortObject(o[k])]);
  30. }
  31. return values;
  32. }
  33. /**
  34. * hash
  35. *
  36. * @param {String} method hash method, e.g.: 'md5', 'sha1'
  37. * @param {String|Buffer} s
  38. * @param {String} [format] output string format, could be 'hex' or 'base64'. default is 'hex'.
  39. * @return {String} md5 hash string
  40. * @public
  41. */
  42. exports.hash = function hash(method, s, format) {
  43. var sum = crypto.createHash(method);
  44. var isBuffer = Buffer.isBuffer(s);
  45. if (!isBuffer && typeof s === 'object') {
  46. s = JSON.stringify(sortObject(s));
  47. }
  48. sum.update(s, isBuffer ? 'binary' : 'utf8');
  49. return sum.digest(format || 'hex');
  50. };
  51. /**
  52. * md5 hash
  53. *
  54. * @param {String|Buffer} s
  55. * @param {String} [format] output string format, could be 'hex' or 'base64'. default is 'hex'.
  56. * @return {String} md5 hash string
  57. * @public
  58. */
  59. exports.md5 = function (s, format) {
  60. return exports.hash('md5', s, format);
  61. };
  62. /**
  63. * sha1 hash
  64. *
  65. * @param {String|Buffer} s
  66. * @param {String} [format] output string format, could be 'hex' or 'base64'. default is 'hex'.
  67. * @return {String} sha1 hash string
  68. * @public
  69. */
  70. exports.sha1 = function (s, format) {
  71. return exports.hash('sha1', s, format);
  72. };
  73. /**
  74. * HMAC algorithm.
  75. *
  76. * Equal bash:
  77. *
  78. * ```bash
  79. * $ echo -n "$data" | openssl dgst -binary -$algorithm -hmac "$key" | openssl $encoding
  80. * ```
  81. *
  82. * @param {String} algorithm, dependent on the available algorithms supported by the version of OpenSSL on the platform.
  83. * Examples are 'sha1', 'md5', 'sha256', 'sha512', etc.
  84. * On recent releases, `openssl list-message-digest-algorithms` will display the available digest algorithms.
  85. * @param {String} key, the hmac key to be used.
  86. * @param {String|Buffer} data, content string.
  87. * @param {String} [encoding='base64']
  88. * @return {String} digest string.
  89. */
  90. exports.hmac = function (algorithm, key, data, encoding) {
  91. encoding = encoding || 'base64';
  92. var hmac = crypto.createHmac(algorithm, key);
  93. hmac.update(data, Buffer.isBuffer(data) ? 'binary' : 'utf8');
  94. return hmac.digest(encoding);
  95. };
  96. /**
  97. * Base64 encode string.
  98. *
  99. * @param {String|Buffer} s
  100. * @param {Boolean} [urlsafe=false] Encode string s using a URL-safe alphabet,
  101. * which substitutes - instead of + and _ instead of / in the standard Base64 alphabet.
  102. * @return {String} base64 encode format string.
  103. */
  104. exports.base64encode = function (s, urlsafe) {
  105. if (!Buffer.isBuffer(s)) {
  106. s = new Buffer(s);
  107. }
  108. var encode = s.toString('base64');
  109. if (urlsafe) {
  110. encode = encode.replace(/\+/g, '-').replace(/\//g, '_');
  111. }
  112. return encode;
  113. };
  114. /**
  115. * Base64 string decode.
  116. *
  117. * @param {String} encode, base64 encoding string.
  118. * @param {Boolean} [urlsafe=false] Decode string s using a URL-safe alphabet,
  119. * which substitutes - instead of + and _ instead of / in the standard Base64 alphabet.
  120. * @return {String} plain text.
  121. */
  122. exports.base64decode = function (encode, urlsafe) {
  123. if (urlsafe) {
  124. encode = encode.replace(/\-/g, '+').replace(/_/g, '/');
  125. }
  126. encode = new Buffer(encode, 'base64');
  127. return encode.toString();
  128. };
  129. /**
  130. * Escape the given string of `html`.
  131. *
  132. * @param {String} html
  133. * @return {String}
  134. * @public
  135. */
  136. exports.escape = function (html) {
  137. return String(html)
  138. .replace(/&(?!\w+;)/g, '&amp;')
  139. .replace(/</g, '&lt;')
  140. .replace(/>/g, '&gt;')
  141. .replace(/"/g, '&quot;');
  142. };
  143. /**
  144. * Array random slice with items count.
  145. * @param {Array} arr
  146. * @param {Number} num, number of sub items.
  147. * @return {Array}
  148. */
  149. exports.randomSlice = function (arr, num) {
  150. if (!num || num >= arr.length) {
  151. return arr.slice();
  152. }
  153. var index = Math.floor(Math.random() * arr.length);
  154. var a = [];
  155. for (var i = 0, j = index; i < num; i++) {
  156. a.push(arr[j++]);
  157. if (j === arr.length) {
  158. j = 0;
  159. }
  160. }
  161. return a;
  162. };
  163. /**
  164. * Safe encodeURIComponent, won't throw any error.
  165. * If `encodeURIComponent` error happen, just return the original value.
  166. *
  167. * @param {String} text
  168. * @return {String} URL encode string.
  169. */
  170. exports.encodeURIComponent = function (text) {
  171. try {
  172. return encodeURIComponent(text);
  173. } catch (e) {
  174. return text;
  175. }
  176. };
  177. /**
  178. * Safe decodeURIComponent, won't throw any error.
  179. * If `decodeURIComponent` error happen, just return the original value.
  180. *
  181. * @param {String} encodeText
  182. * @return {String} URL decode original string.
  183. */
  184. exports.decodeURIComponent = function (encodeText) {
  185. try {
  186. return decodeURIComponent(encodeText);
  187. } catch (e) {
  188. return encodeText;
  189. }
  190. };
  191. var MONTHS = [
  192. 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
  193. ];
  194. // only set once.
  195. var TIMEZONE = ' ';
  196. var _hourOffset = parseInt(-(new Date().getTimezoneOffset()) / 60, 10);
  197. if (_hourOffset >= 0) {
  198. TIMEZONE += '+';
  199. } else {
  200. TIMEZONE += '-';
  201. }
  202. _hourOffset = Math.abs(_hourOffset);
  203. if (_hourOffset < 10) {
  204. _hourOffset = '0' + _hourOffset;
  205. }
  206. TIMEZONE += _hourOffset + '00';
  207. /**
  208. * Access log format date. format: `moment().format('DD/MMM/YYYY:HH:mm:ss ZZ')`
  209. *
  210. * @return {String}
  211. */
  212. exports.accessLogDate = function (d) {
  213. // 16/Apr/2013:16:40:09 +0800
  214. d = d || new Date();
  215. var date = d.getDate();
  216. if (date < 10) {
  217. date = '0' + date;
  218. }
  219. var hours = d.getHours();
  220. if (hours < 10) {
  221. hours = '0' + hours;
  222. }
  223. var mintues = d.getMinutes();
  224. if (mintues < 10) {
  225. mintues = '0' + mintues;
  226. }
  227. var seconds = d.getSeconds();
  228. if (seconds < 10) {
  229. seconds = '0' + seconds;
  230. }
  231. return date + '/' + MONTHS[d.getMonth()] + '/' + d.getFullYear() +
  232. ':' + hours + ':' + mintues + ':' + seconds + TIMEZONE;
  233. };
  234. /**
  235. * Normal log format date. format: `moment().format('YYYY-MM-DD HH:mm:ss.SSS')`
  236. *
  237. * @return {String}
  238. */
  239. exports.logDate = exports.YYYYMMDDHHmmssSSS = function (d) {
  240. d = d || new Date();
  241. var date = d.getDate();
  242. if (date < 10) {
  243. date = '0' + date;
  244. }
  245. var month = d.getMonth() + 1;
  246. if (month < 10) {
  247. month = '0' + month;
  248. }
  249. var hours = d.getHours();
  250. if (hours < 10) {
  251. hours = '0' + hours;
  252. }
  253. var mintues = d.getMinutes();
  254. if (mintues < 10) {
  255. mintues = '0' + mintues;
  256. }
  257. var seconds = d.getSeconds();
  258. if (seconds < 10) {
  259. seconds = '0' + seconds;
  260. }
  261. var milliseconds = d.getMilliseconds();
  262. if (milliseconds < 10) {
  263. milliseconds = '00' + milliseconds;
  264. } else if (milliseconds < 100) {
  265. milliseconds = '0' + milliseconds;
  266. }
  267. return d.getFullYear() + '-' + month + '-' + date + ' ' +
  268. hours + ':' + mintues + ':' + seconds + '.' + milliseconds;
  269. };
  270. /**
  271. * `moment().format('YYYY-MM-DD HH:mm:ss')` format date string.
  272. *
  273. * @return {String}
  274. */
  275. exports.YYYYMMDDHHmmss = function (d) {
  276. d = d || new Date();
  277. var date = d.getDate();
  278. if (date < 10) {
  279. date = '0' + date;
  280. }
  281. var month = d.getMonth() + 1;
  282. if (month < 10) {
  283. month = '0' + month;
  284. }
  285. var hours = d.getHours();
  286. if (hours < 10) {
  287. hours = '0' + hours;
  288. }
  289. var mintues = d.getMinutes();
  290. if (mintues < 10) {
  291. mintues = '0' + mintues;
  292. }
  293. var seconds = d.getSeconds();
  294. if (seconds < 10) {
  295. seconds = '0' + seconds;
  296. }
  297. return d.getFullYear() + '-' + month + '-' + date + ' ' +
  298. hours + ':' + mintues + ':' + seconds;
  299. };
  300. /**
  301. * `moment().format('YYYY-MM-DD')` format date string.
  302. *
  303. * @return {String}
  304. */
  305. exports.YYYYMMDD = function YYYYMMDD(d) {
  306. d = d || new Date();
  307. var date = d.getDate();
  308. if (date < 10) {
  309. date = '0' + date;
  310. }
  311. var month = d.getMonth() + 1;
  312. if (month < 10) {
  313. month = '0' + month;
  314. }
  315. return d.getFullYear() + '-' + month + '-' + date;
  316. };
  317. /**
  318. * return datetime struct.
  319. *
  320. * @return {Object} date
  321. * - {Number} YYYYMMDD, 20130401
  322. * - {Number} H, 0, 1, 9, 12, 23
  323. */
  324. exports.datestruct = function (now) {
  325. now = now || new Date();
  326. return {
  327. YYYYMMDD: now.getFullYear() * 10000 + (now.getMonth() + 1) * 100 + now.getDate(),
  328. H: now.getHours()
  329. };
  330. };
  331. var _showWarnning = false;
  332. /**
  333. * Get current machine IPv4
  334. *
  335. * @param {String} [interfaceName] interface name, default is 'eth' on linux, 'en' on mac os.
  336. * @return {String} IP address
  337. */
  338. exports.getIP = exports.getIPv4 = function (interfaceName) {
  339. if (!_showWarnning) {
  340. _showWarnning = true;
  341. console.warn('[WARNNING] getIP() remove, PLEASE use `https://github.com/fengmk2/address` module instead');
  342. }
  343. return address.ip(interfaceName);
  344. };
  345. /**
  346. * Get current machine IPv6
  347. *
  348. * @param {String} [interfaceName] interface name, default is 'eth' on linux, 'en' on mac os.
  349. * @return {String} IP address
  350. */
  351. exports.getIPv6 = function (interfaceName) {
  352. return address.ipv6(interfaceName);
  353. };
  354. /**
  355. * Get a function parameter's names.
  356. *
  357. * @param {Function} func
  358. * @param {Boolean} [useCache], default is true
  359. * @return {Array} names
  360. */
  361. exports.getParamNames = function (func, cache) {
  362. cache = cache !== false;
  363. if (cache && func.__cache_names) {
  364. return func.__cache_names;
  365. }
  366. var str = func.toString();
  367. var names = str.slice(str.indexOf('(') + 1, str.indexOf(')')).match(/([^\s,]+)/g) || [];
  368. func.__cache_names = names;
  369. return names;
  370. };
  371. // http://www.2ality.com/2013/10/safe-integers.html
  372. exports.MAX_SAFE_INTEGER = Math.pow(2, 53) - 1;
  373. exports.MIN_SAFE_INTEGER = -exports.MAX_SAFE_INTEGER;
  374. var MAX_SAFE_INTEGER_STR = exports.MAX_SAFE_INTEGER_STR = String(exports.MAX_SAFE_INTEGER);
  375. var MAX_SAFE_INTEGER_STR_LENGTH = MAX_SAFE_INTEGER_STR.length;
  376. /**
  377. * Detect a number string can safe convert to Javascript Number.
  378. *
  379. * @param {String} s number format string, like `"123"`, `"-1000123123123123123123"`
  380. * @return {Boolean}
  381. */
  382. exports.isSafeNumberString = function (s) {
  383. if (s[0] === '-') {
  384. s = s.substring(1);
  385. }
  386. if (s.length < MAX_SAFE_INTEGER_STR_LENGTH || (s.length === MAX_SAFE_INTEGER_STR_LENGTH && s <= MAX_SAFE_INTEGER_STR)) {
  387. return true;
  388. }
  389. return false;
  390. };
  391. /**
  392. * Convert string to Number if string in safe Number scope.
  393. *
  394. * @param {String} s number format string.
  395. * @return {Number|String} success will return Number, otherise return the original string.
  396. */
  397. exports.toSafeNumber = function (s) {
  398. if (typeof s === 'number') {
  399. return s;
  400. }
  401. return exports.isSafeNumberString(s) ? Number(s) : s;
  402. };
  403. /**
  404. * Get Unix's timestamp in seconds.
  405. * @return {Number}
  406. */
  407. exports.timestamp = function (t) {
  408. if (t) {
  409. var v = t;
  410. if (typeof v === 'string') {
  411. v = Number(v);
  412. }
  413. if (String(t).length === 10) {
  414. v *= 1000;
  415. }
  416. return new Date(v);
  417. }
  418. return Math.round(Date.now() / 1000);
  419. };
  420. var _setImmediate = typeof setImmediate === 'function' ? setImmediate : process.nextTick;
  421. exports.setImmediate = function (callback) {
  422. _setImmediate(callback);
  423. };
  424. exports.randomString = function (length, charSet) {
  425. var result = [];
  426. length = length || 16;
  427. charSet = charSet || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  428. while (length--) {
  429. result.push(charSet[Math.floor(Math.random() * charSet.length)]);
  430. }
  431. return result.join('');
  432. };
  433. exports.has = function (obj, prop) {
  434. return Object.prototype.hasOwnProperty.call(obj, prop);
  435. };