Sequence.js 3.2 KB

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