comunicationLeap/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}