socketioMEANseed/server/node_modules/socket.io-client/lib/socket.js
server/node_modules/socket.io-client/lib/socket.jsRaw
1
2/**
3 * Module dependencies.
4 */
5
6var parser = require('socket.io-parser');
7var Emitter = require('component-emitter');
8var toArray = require('to-array');
9var on = require('./on');
10var bind = require('component-bind');
11var debug = require('debug')('socket.io-client:socket');
12var hasBin = require('has-binary');
13
14/**
15 * Module exports.
16 */
17
18module.exports = exports = Socket;
19
20/**
21 * Internal events (blacklisted).
22 * These events can't be emitted by the user.
23 *
24 * @api private
25 */
26
27var events = {
28 connect: 1,
29 connect_error: 1,
30 connect_timeout: 1,
31 connecting: 1,
32 disconnect: 1,
33 error: 1,
34 reconnect: 1,
35 reconnect_attempt: 1,
36 reconnect_failed: 1,
37 reconnect_error: 1,
38 reconnecting: 1,
39 ping: 1,
40 pong: 1
41};
42
43/**
44 * Shortcut to `Emitter#emit`.
45 */
46
47var emit = Emitter.prototype.emit;
48
49/**
50 * `Socket` constructor.
51 *
52 * @api public
53 */
54
55function Socket (io, nsp, opts) {
56 this.io = io;
57 this.nsp = nsp;
58 this.json = this; // compat
59 this.ids = 0;
60 this.acks = {};
61 this.receiveBuffer = [];
62 this.sendBuffer = [];
63 this.connected = false;
64 this.disconnected = true;
65 if (opts && opts.query) {
66 this.query = opts.query;
67 }
68 if (this.io.autoConnect) this.open();
69}
70
71/**
72 * Mix in `Emitter`.
73 */
74
75Emitter(Socket.prototype);
76
77/**
78 * Subscribe to open, close and packet events
79 *
80 * @api private
81 */
82
83Socket.prototype.subEvents = function () {
84 if (this.subs) return;
85
86 var io = this.io;
87 this.subs = [
88 on(io, 'open', bind(this, 'onopen')),
89 on(io, 'packet', bind(this, 'onpacket')),
90 on(io, 'close', bind(this, 'onclose'))
91 ];
92};
93
94/**
95 * "Opens" the socket.
96 *
97 * @api public
98 */
99
100Socket.prototype.open =
101Socket.prototype.connect = function () {
102 if (this.connected) return this;
103
104 this.subEvents();
105 this.io.open(); // ensure open
106 if ('open' === this.io.readyState) this.onopen();
107 this.emit('connecting');
108 return this;
109};
110
111/**
112 * Sends a `message` event.
113 *
114 * @return {Socket} self
115 * @api public
116 */
117
118Socket.prototype.send = function () {
119 var args = toArray(arguments);
120 args.unshift('message');
121 this.emit.apply(this, args);
122 return this;
123};
124
125/**
126 * Override `emit`.
127 * If the event is in `events`, it's emitted normally.
128 *
129 * @param {String} event name
130 * @return {Socket} self
131 * @api public
132 */
133
134Socket.prototype.emit = function (ev) {
135 if (events.hasOwnProperty(ev)) {
136 emit.apply(this, arguments);
137 return this;
138 }
139
140 var args = toArray(arguments);
141 var parserType = parser.EVENT; // default
142 if (hasBin(args)) { parserType = parser.BINARY_EVENT; } // binary
143 var packet = { type: parserType, data: args };
144
145 packet.options = {};
146 packet.options.compress = !this.flags || false !== this.flags.compress;
147
148 // event ack callback
149 if ('function' === typeof args[args.length - 1]) {
150 debug('emitting packet with ack id %d', this.ids);
151 this.acks[this.ids] = args.pop();
152 packet.id = this.ids++;
153 }
154
155 if (this.connected) {
156 this.packet(packet);
157 } else {
158 this.sendBuffer.push(packet);
159 }
160
161 delete this.flags;
162
163 return this;
164};
165
166/**
167 * Sends a packet.
168 *
169 * @param {Object} packet
170 * @api private
171 */
172
173Socket.prototype.packet = function (packet) {
174 packet.nsp = this.nsp;
175 this.io.packet(packet);
176};
177
178/**
179 * Called upon engine `open`.
180 *
181 * @api private
182 */
183
184Socket.prototype.onopen = function () {
185 debug('transport is open - connecting');
186
187 // write connect packet if necessary
188 if ('/' !== this.nsp) {
189 if (this.query) {
190 this.packet({type: parser.CONNECT, query: this.query});
191 } else {
192 this.packet({type: parser.CONNECT});
193 }
194 }
195};
196
197/**
198 * Called upon engine `close`.
199 *
200 * @param {String} reason
201 * @api private
202 */
203
204Socket.prototype.onclose = function (reason) {
205 debug('close (%s)', reason);
206 this.connected = false;
207 this.disconnected = true;
208 delete this.id;
209 this.emit('disconnect', reason);
210};
211
212/**
213 * Called with socket packet.
214 *
215 * @param {Object} packet
216 * @api private
217 */
218
219Socket.prototype.onpacket = function (packet) {
220 if (packet.nsp !== this.nsp) return;
221
222 switch (packet.type) {
223 case parser.CONNECT:
224 this.onconnect();
225 break;
226
227 case parser.EVENT:
228 this.onevent(packet);
229 break;
230
231 case parser.BINARY_EVENT:
232 this.onevent(packet);
233 break;
234
235 case parser.ACK:
236 this.onack(packet);
237 break;
238
239 case parser.BINARY_ACK:
240 this.onack(packet);
241 break;
242
243 case parser.DISCONNECT:
244 this.ondisconnect();
245 break;
246
247 case parser.ERROR:
248 this.emit('error', packet.data);
249 break;
250 }
251};
252
253/**
254 * Called upon a server event.
255 *
256 * @param {Object} packet
257 * @api private
258 */
259
260Socket.prototype.onevent = function (packet) {
261 var args = packet.data || [];
262 debug('emitting event %j', args);
263
264 if (null != packet.id) {
265 debug('attaching ack callback to event');
266 args.push(this.ack(packet.id));
267 }
268
269 if (this.connected) {
270 emit.apply(this, args);
271 } else {
272 this.receiveBuffer.push(args);
273 }
274};
275
276/**
277 * Produces an ack callback to emit with an event.
278 *
279 * @api private
280 */
281
282Socket.prototype.ack = function (id) {
283 var self = this;
284 var sent = false;
285 return function () {
286 // prevent double callbacks
287 if (sent) return;
288 sent = true;
289 var args = toArray(arguments);
290 debug('sending ack %j', args);
291
292 var type = hasBin(args) ? parser.BINARY_ACK : parser.ACK;
293 self.packet({
294 type: type,
295 id: id,
296 data: args
297 });
298 };
299};
300
301/**
302 * Called upon a server acknowlegement.
303 *
304 * @param {Object} packet
305 * @api private
306 */
307
308Socket.prototype.onack = function (packet) {
309 var ack = this.acks[packet.id];
310 if ('function' === typeof ack) {
311 debug('calling ack %s with %j', packet.id, packet.data);
312 ack.apply(this, packet.data);
313 delete this.acks[packet.id];
314 } else {
315 debug('bad ack %s', packet.id);
316 }
317};
318
319/**
320 * Called upon server connect.
321 *
322 * @api private
323 */
324
325Socket.prototype.onconnect = function () {
326 this.connected = true;
327 this.disconnected = false;
328 this.emit('connect');
329 this.emitBuffered();
330};
331
332/**
333 * Emit buffered events (received and emitted).
334 *
335 * @api private
336 */
337
338Socket.prototype.emitBuffered = function () {
339 var i;
340 for (i = 0; i < this.receiveBuffer.length; i++) {
341 emit.apply(this, this.receiveBuffer[i]);
342 }
343 this.receiveBuffer = [];
344
345 for (i = 0; i < this.sendBuffer.length; i++) {
346 this.packet(this.sendBuffer[i]);
347 }
348 this.sendBuffer = [];
349};
350
351/**
352 * Called upon server disconnect.
353 *
354 * @api private
355 */
356
357Socket.prototype.ondisconnect = function () {
358 debug('server disconnect (%s)', this.nsp);
359 this.destroy();
360 this.onclose('io server disconnect');
361};
362
363/**
364 * Called upon forced client/server side disconnections,
365 * this method ensures the manager stops tracking us and
366 * that reconnections don't get triggered for this.
367 *
368 * @api private.
369 */
370
371Socket.prototype.destroy = function () {
372 if (this.subs) {
373 // clean subscriptions to avoid reconnections
374 for (var i = 0; i < this.subs.length; i++) {
375 this.subs[i].destroy();
376 }
377 this.subs = null;
378 }
379
380 this.io.destroy(this);
381};
382
383/**
384 * Disconnects the socket manually.
385 *
386 * @return {Socket} self
387 * @api public
388 */
389
390Socket.prototype.close =
391Socket.prototype.disconnect = function () {
392 if (this.connected) {
393 debug('performing disconnect (%s)', this.nsp);
394 this.packet({ type: parser.DISCONNECT });
395 }
396
397 // remove socket from pool
398 this.destroy();
399
400 if (this.connected) {
401 // fire events
402 this.onclose('io client disconnect');
403 }
404 return this;
405};
406
407/**
408 * Sets the compress flag.
409 *
410 * @param {Boolean} if `true`, compresses the sending data
411 * @return {Socket} self
412 * @api public
413 */
414
415Socket.prototype.compress = function (compress) {
416 this.flags = this.flags || {};
417 this.flags.compress = compress;
418 return this;
419};