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.

123 lines
2.9 KiB

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