You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

124 lines
2.9 KiB

7 years ago
  1. /*!
  2. * ws: a node.js websocket client
  3. * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
  4. * MIT Licensed
  5. */
  6. var events = require('events')
  7. , util = require('util')
  8. , EventEmitter = events.EventEmitter;
  9. /**
  10. * Hixie Sender implementation
  11. */
  12. function Sender(socket) {
  13. if (this instanceof Sender === false) {
  14. throw new TypeError("Classes can't be function-called");
  15. }
  16. events.EventEmitter.call(this);
  17. this.socket = socket;
  18. this.continuationFrame = false;
  19. this.isClosed = false;
  20. }
  21. module.exports = Sender;
  22. /**
  23. * Inherits from EventEmitter.
  24. */
  25. util.inherits(Sender, events.EventEmitter);
  26. /**
  27. * Frames and writes data.
  28. *
  29. * @api public
  30. */
  31. Sender.prototype.send = function(data, options, cb) {
  32. if (this.isClosed) return;
  33. var isString = typeof data == 'string'
  34. , length = isString ? Buffer.byteLength(data) : data.length
  35. , lengthbytes = (length > 127) ? 2 : 1 // assume less than 2**14 bytes
  36. , writeStartMarker = this.continuationFrame == false
  37. , writeEndMarker = !options || !(typeof options.fin != 'undefined' && !options.fin)
  38. , buffer = new Buffer((writeStartMarker ? ((options && options.binary) ? (1 + lengthbytes) : 1) : 0) + length + ((writeEndMarker && !(options && options.binary)) ? 1 : 0))
  39. , offset = writeStartMarker ? 1 : 0;
  40. if (writeStartMarker) {
  41. if (options && options.binary) {
  42. buffer.write('\x80', 'binary');
  43. // assume length less than 2**14 bytes
  44. if (lengthbytes > 1)
  45. buffer.write(String.fromCharCode(128+length/128), offset++, 'binary');
  46. buffer.write(String.fromCharCode(length&0x7f), offset++, 'binary');
  47. } else
  48. buffer.write('\x00', 'binary');
  49. }
  50. if (isString) buffer.write(data, offset, 'utf8');
  51. else data.copy(buffer, offset, 0);
  52. if (writeEndMarker) {
  53. if (options && options.binary) {
  54. // sending binary, not writing end marker
  55. } else
  56. buffer.write('\xff', offset + length, 'binary');
  57. this.continuationFrame = false;
  58. }
  59. else this.continuationFrame = true;
  60. try {
  61. this.socket.write(buffer, 'binary', cb);
  62. } catch (e) {
  63. this.error(e.toString());
  64. }
  65. };
  66. /**
  67. * Sends a close instruction to the remote party.
  68. *
  69. * @api public
  70. */
  71. Sender.prototype.close = function(code, data, mask, cb) {
  72. if (this.isClosed) return;
  73. this.isClosed = true;
  74. try {
  75. if (this.continuationFrame) this.socket.write(new Buffer([0xff], 'binary'));
  76. this.socket.write(new Buffer([0xff, 0x00]), 'binary', cb);
  77. } catch (e) {
  78. this.error(e.toString());
  79. }
  80. };
  81. /**
  82. * Sends a ping message to the remote party. Not available for hixie.
  83. *
  84. * @api public
  85. */
  86. Sender.prototype.ping = function(data, options) {};
  87. /**
  88. * Sends a pong message to the remote party. Not available for hixie.
  89. *
  90. * @api public
  91. */
  92. Sender.prototype.pong = function(data, options) {};
  93. /**
  94. * Handles an error
  95. *
  96. * @api private
  97. */
  98. Sender.prototype.error = function (reason) {
  99. this.emit('error', reason);
  100. return this;
  101. };