arnaucube
arnaucube/raspberryGPIOhtmlserverPublic Self-hosted repository
raspberryGPIOhtmlserver/node_modules/ws/lib/Sender.js
node_modules/ws/lib/Sender.jsRaw
  1/*!
  2 * ws: a node.js websocket client
  3 * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
  4 * MIT Licensed
  5 */
  6
  7var events = require('events')
  8  , util = require('util')
  9  , EventEmitter = events.EventEmitter
 10  , ErrorCodes = require('./ErrorCodes')
 11  , bufferUtil = require('./BufferUtil').BufferUtil;
 12
 13/**
 14 * HyBi Sender implementation
 15 */
 16
 17function Sender(socket) {
 18  this._socket = socket;
 19  this.firstFragment = true;
 20}
 21
 22/**
 23 * Inherits from EventEmitter.
 24 */
 25
 26util.inherits(Sender, events.EventEmitter);
 27
 28/**
 29 * Sends a close instruction to the remote party.
 30 *
 31 * @api public
 32 */
 33
 34Sender.prototype.close = function(code, data, mask) {
 35  if (typeof code !== 'undefined') {
 36    if (typeof code !== 'number' ||
 37      !ErrorCodes.isValidErrorCode(code)) throw new Error('first argument must be a valid error code number');
 38  }
 39  code = code || 1000;
 40  var dataBuffer = new Buffer(2 + (data ? Buffer.byteLength(data) : 0));
 41  writeUInt16BE.call(dataBuffer, code, 0);
 42  if (dataBuffer.length > 2) dataBuffer.write(data, 2);
 43  this.frameAndSend(0x8, dataBuffer, true, mask);
 44}
 45
 46/**
 47 * Sends a ping message to the remote party.
 48 *
 49 * @api public
 50 */
 51
 52Sender.prototype.ping = function(data, options) {
 53  var mask = options && options.mask;
 54  this.frameAndSend(0x9, data || '', true, mask);
 55}
 56
 57/**
 58 * Sends a pong message to the remote party.
 59 *
 60 * @api public
 61 */
 62
 63Sender.prototype.pong = function(data, options) {
 64  var mask = options && options.mask;
 65  this.frameAndSend(0xa, data || '', true, mask);
 66}
 67
 68/**
 69 * Sends text or binary data to the remote party.
 70 *
 71 * @api public
 72 */
 73
 74Sender.prototype.send = function(data, options, cb) {
 75  var finalFragment = options && options.fin === false ? false : true;
 76  var mask = options && options.mask;
 77  var opcode = options && options.binary ? 2 : 1;
 78  if (this.firstFragment === false) opcode = 0;
 79  else this.firstFragment = false;
 80  if (finalFragment) this.firstFragment = true
 81  this.frameAndSend(opcode, data, finalFragment, mask, cb);
 82}
 83
 84/**
 85 * Frames and sends a piece of data according to the HyBi WebSocket protocol.
 86 *
 87 * @api private
 88 */
 89
 90Sender.prototype.frameAndSend = function(opcode, data, finalFragment, maskData, cb) {
 91  var canModifyData = false;
 92
 93  if (!data) {
 94    try {
 95      this._socket.write(new Buffer([opcode | (finalFragment ? 0x80 : 0), 0 | (maskData ? 0x80 : 0)].concat(maskData ? [0, 0, 0, 0] : [])), 'binary', cb);
 96    }
 97    catch (e) {
 98      if (typeof cb == 'function') cb(e);
 99      else this.emit('error', e);
100    }
101    return;
102  }
103
104  if (!Buffer.isBuffer(data)) {
105    canModifyData = true;
106    if (data && (typeof data.byteLength !== 'undefined' || typeof data.buffer !== 'undefined')) {
107      data = getArrayBuffer(data);
108    } else {
109      data = new Buffer(data);
110    }
111  }
112
113  var dataLength = data.length
114    , dataOffset = maskData ? 6 : 2
115    , secondByte = dataLength;
116
117  if (dataLength >= 65536) {
118    dataOffset += 8;
119    secondByte = 127;
120  }
121  else if (dataLength > 125) {
122    dataOffset += 2;
123    secondByte = 126;
124  }
125
126  var mergeBuffers = dataLength < 32768 || (maskData && !canModifyData);
127  var totalLength = mergeBuffers ? dataLength + dataOffset : dataOffset;
128  var outputBuffer = new Buffer(totalLength);
129  outputBuffer[0] = finalFragment ? opcode | 0x80 : opcode;
130
131  switch (secondByte) {
132    case 126:
133      writeUInt16BE.call(outputBuffer, dataLength, 2);
134      break;
135    case 127:
136      writeUInt32BE.call(outputBuffer, 0, 2);
137      writeUInt32BE.call(outputBuffer, dataLength, 6);
138  }
139
140  if (maskData) {
141    outputBuffer[1] = secondByte | 0x80;
142    var mask = this._randomMask || (this._randomMask = getRandomMask());
143    outputBuffer[dataOffset - 4] = mask[0];
144    outputBuffer[dataOffset - 3] = mask[1];
145    outputBuffer[dataOffset - 2] = mask[2];
146    outputBuffer[dataOffset - 1] = mask[3];
147    if (mergeBuffers) {
148      bufferUtil.mask(data, mask, outputBuffer, dataOffset, dataLength);
149      try {
150        this._socket.write(outputBuffer, 'binary', cb);
151      }
152      catch (e) {
153        if (typeof cb == 'function') cb(e);
154        else this.emit('error', e);
155      }
156    }
157    else {
158      bufferUtil.mask(data, mask, data, 0, dataLength);
159      try {
160        this._socket.write(outputBuffer, 'binary');
161        this._socket.write(data, 'binary', cb);
162      }
163      catch (e) {
164        if (typeof cb == 'function') cb(e);
165        else this.emit('error', e);
166      }
167    }
168  }
169  else {
170    outputBuffer[1] = secondByte;
171    if (mergeBuffers) {
172      data.copy(outputBuffer, dataOffset);
173      try {
174        this._socket.write(outputBuffer, 'binary', cb);
175      }
176      catch (e) {
177        if (typeof cb == 'function') cb(e);
178        else this.emit('error', e);
179      }
180    }
181    else {
182      try {
183        this._socket.write(outputBuffer, 'binary');
184        this._socket.write(data, 'binary', cb);
185      }
186      catch (e) {
187        if (typeof cb == 'function') cb(e);
188        else this.emit('error', e);
189      }
190    }
191  }
192}
193
194module.exports = Sender;
195
196function writeUInt16BE(value, offset) {
197  this[offset] = (value & 0xff00)>>8;
198  this[offset+1] = value & 0xff;
199}
200
201function writeUInt32BE(value, offset) {
202  this[offset] = (value & 0xff000000)>>24;
203  this[offset+1] = (value & 0xff0000)>>16;
204  this[offset+2] = (value & 0xff00)>>8;
205  this[offset+3] = value & 0xff;
206}
207
208function getArrayBuffer(data) {
209  // data is either an ArrayBuffer or ArrayBufferView.
210  var array = new Uint8Array(data.buffer || data)
211    , l = data.byteLength || data.length
212    , o = data.byteOffset || 0
213    , buffer = new Buffer(l);
214  for (var i = 0; i < l; ++i) {
215    buffer[i] = array[o+i];
216  }
217  return buffer;
218}
219
220function getRandomMask() {
221  return new Buffer([
222    ~~(Math.random() * 255),
223    ~~(Math.random() * 255),
224    ~~(Math.random() * 255),
225    ~~(Math.random() * 255)
226  ]);
227}