Sequence.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. var Util = require('util');
  2. var EventEmitter = require('events').EventEmitter;
  3. var Packets = require('../packets');
  4. var ErrorConstants = require('../constants/errors');
  5. // istanbul ignore next: Node.js < 0.10 not covered
  6. var listenerCount = EventEmitter.listenerCount
  7. || function(emitter, type){ return emitter.listeners(type).length; };
  8. var LONG_STACK_DELIMITER = '\n --------------------\n';
  9. module.exports = Sequence;
  10. Util.inherits(Sequence, EventEmitter);
  11. function Sequence(options, callback) {
  12. if (typeof options === 'function') {
  13. callback = options;
  14. options = {};
  15. }
  16. EventEmitter.call(this);
  17. options = options || {};
  18. this._callback = callback;
  19. this._callSite = null;
  20. this._ended = false;
  21. this._timeout = options.timeout;
  22. // For Timers
  23. this._idleNext = null;
  24. this._idlePrev = null;
  25. this._idleStart = null;
  26. this._idleTimeout = -1;
  27. this._repeat = null;
  28. }
  29. Sequence.determinePacket = function(byte) {
  30. switch (byte) {
  31. case 0x00: return Packets.OkPacket;
  32. case 0xfe: return Packets.EofPacket;
  33. case 0xff: return Packets.ErrorPacket;
  34. default: return undefined;
  35. }
  36. };
  37. Sequence.prototype.hasErrorHandler = function() {
  38. return Boolean(this._callback) || listenerCount(this, 'error') > 1;
  39. };
  40. Sequence.prototype._packetToError = function(packet) {
  41. var code = ErrorConstants[packet.errno] || 'UNKNOWN_CODE_PLEASE_REPORT';
  42. var err = new Error(code + ': ' + packet.message);
  43. err.code = code;
  44. err.errno = packet.errno;
  45. err.sqlMessage = packet.message;
  46. err.sqlState = packet.sqlState;
  47. return err;
  48. };
  49. Sequence.prototype.end = function(err) {
  50. if (this._ended) {
  51. return;
  52. }
  53. this._ended = true;
  54. if (err) {
  55. this._addLongStackTrace(err);
  56. }
  57. // Without this we are leaking memory. This problem was introduced in
  58. // 8189925374e7ce3819bbe88b64c7b15abac96b16. I suspect that the error object
  59. // causes a cyclic reference that the GC does not detect properly, but I was
  60. // unable to produce a standalone version of this leak. This would be a great
  61. // challenge for somebody interested in difficult problems : )!
  62. this._callSite = null;
  63. // try...finally for exception safety
  64. try {
  65. if (err) {
  66. this.emit('error', err);
  67. }
  68. } finally {
  69. try {
  70. if (this._callback) {
  71. this._callback.apply(this, arguments);
  72. }
  73. } finally {
  74. this.emit('end');
  75. }
  76. }
  77. };
  78. Sequence.prototype['OkPacket'] = function(packet) {
  79. this.end(null, packet);
  80. };
  81. Sequence.prototype['ErrorPacket'] = function(packet) {
  82. this.end(this._packetToError(packet));
  83. };
  84. // Implemented by child classes
  85. Sequence.prototype.start = function() {};
  86. Sequence.prototype._addLongStackTrace = function _addLongStackTrace(err) {
  87. var callSiteStack = this._callSite && this._callSite.stack;
  88. if (!callSiteStack || typeof callSiteStack !== 'string') {
  89. // No recorded call site
  90. return;
  91. }
  92. if (err.stack.indexOf(LONG_STACK_DELIMITER) !== -1) {
  93. // Error stack already looks long
  94. return;
  95. }
  96. var index = callSiteStack.indexOf('\n');
  97. if (index !== -1) {
  98. // Append recorded call site
  99. err.stack += LONG_STACK_DELIMITER + callSiteStack.substr(index + 1);
  100. }
  101. };
  102. Sequence.prototype._onTimeout = function _onTimeout() {
  103. this.emit('timeout');
  104. };