[SignalingServer] Optimize dependent modules
[platform/framework/web/wrtjs.git] / device_home / node_modules / engine.io-client / lib / transports / websocket.js
1 /**
2  * Module dependencies.
3  */
4
5 var Transport = require('../transport');
6 var parser = require('engine.io-parser');
7 var parseqs = require('parseqs');
8 var inherit = require('component-inherit');
9 var yeast = require('yeast');
10 var debug = require('debug')('engine.io-client:websocket');
11
12 var BrowserWebSocket, NodeWebSocket;
13
14 if (typeof WebSocket !== 'undefined') {
15   BrowserWebSocket = WebSocket;
16 } else if (typeof self !== 'undefined') {
17   BrowserWebSocket = self.WebSocket || self.MozWebSocket;
18 }
19
20 if (typeof window === 'undefined') {
21   try {
22     NodeWebSocket = require('ws');
23   } catch (e) { }
24 }
25
26 /**
27  * Get either the `WebSocket` or `MozWebSocket` globals
28  * in the browser or try to resolve WebSocket-compatible
29  * interface exposed by `ws` for Node-like environment.
30  */
31
32 var WebSocketImpl = BrowserWebSocket || NodeWebSocket;
33
34 /**
35  * Module exports.
36  */
37
38 module.exports = WS;
39
40 /**
41  * WebSocket transport constructor.
42  *
43  * @api {Object} connection options
44  * @api public
45  */
46
47 function WS (opts) {
48   var forceBase64 = (opts && opts.forceBase64);
49   if (forceBase64) {
50     this.supportsBinary = false;
51   }
52   this.perMessageDeflate = opts.perMessageDeflate;
53   this.usingBrowserWebSocket = BrowserWebSocket && !opts.forceNode;
54   this.protocols = opts.protocols;
55   if (!this.usingBrowserWebSocket) {
56     WebSocketImpl = NodeWebSocket;
57   }
58   Transport.call(this, opts);
59 }
60
61 /**
62  * Inherits from Transport.
63  */
64
65 inherit(WS, Transport);
66
67 /**
68  * Transport name.
69  *
70  * @api public
71  */
72
73 WS.prototype.name = 'websocket';
74
75 /*
76  * WebSockets support binary
77  */
78
79 WS.prototype.supportsBinary = true;
80
81 /**
82  * Opens socket.
83  *
84  * @api private
85  */
86
87 WS.prototype.doOpen = function () {
88   if (!this.check()) {
89     // let probe timeout
90     return;
91   }
92
93   var uri = this.uri();
94   var protocols = this.protocols;
95
96   var opts = {};
97
98   if (!this.isReactNative) {
99     opts.agent = this.agent;
100     opts.perMessageDeflate = this.perMessageDeflate;
101
102     // SSL options for Node.js client
103     opts.pfx = this.pfx;
104     opts.key = this.key;
105     opts.passphrase = this.passphrase;
106     opts.cert = this.cert;
107     opts.ca = this.ca;
108     opts.ciphers = this.ciphers;
109     opts.rejectUnauthorized = this.rejectUnauthorized;
110   }
111
112   if (this.extraHeaders) {
113     opts.headers = this.extraHeaders;
114   }
115   if (this.localAddress) {
116     opts.localAddress = this.localAddress;
117   }
118
119   try {
120     this.ws =
121       this.usingBrowserWebSocket && !this.isReactNative
122         ? protocols
123           ? new WebSocketImpl(uri, protocols)
124           : new WebSocketImpl(uri)
125         : new WebSocketImpl(uri, protocols, opts);
126   } catch (err) {
127     return this.emit('error', err);
128   }
129
130   if (this.ws.binaryType === undefined) {
131     this.supportsBinary = false;
132   }
133
134   if (this.ws.supports && this.ws.supports.binary) {
135     this.supportsBinary = true;
136     this.ws.binaryType = 'nodebuffer';
137   } else {
138     this.ws.binaryType = 'arraybuffer';
139   }
140
141   this.addEventListeners();
142 };
143
144 /**
145  * Adds event listeners to the socket
146  *
147  * @api private
148  */
149
150 WS.prototype.addEventListeners = function () {
151   var self = this;
152
153   this.ws.onopen = function () {
154     self.onOpen();
155   };
156   this.ws.onclose = function () {
157     self.onClose();
158   };
159   this.ws.onmessage = function (ev) {
160     self.onData(ev.data);
161   };
162   this.ws.onerror = function (e) {
163     self.onError('websocket error', e);
164   };
165 };
166
167 /**
168  * Writes data to socket.
169  *
170  * @param {Array} array of packets.
171  * @api private
172  */
173
174 WS.prototype.write = function (packets) {
175   var self = this;
176   this.writable = false;
177
178   // encodePacket efficient as it uses WS framing
179   // no need for encodePayload
180   var total = packets.length;
181   for (var i = 0, l = total; i < l; i++) {
182     (function (packet) {
183       parser.encodePacket(packet, self.supportsBinary, function (data) {
184         if (!self.usingBrowserWebSocket) {
185           // always create a new object (GH-437)
186           var opts = {};
187           if (packet.options) {
188             opts.compress = packet.options.compress;
189           }
190
191           if (self.perMessageDeflate) {
192             var len = 'string' === typeof data ? Buffer.byteLength(data) : data.length;
193             if (len < self.perMessageDeflate.threshold) {
194               opts.compress = false;
195             }
196           }
197         }
198
199         // Sometimes the websocket has already been closed but the browser didn't
200         // have a chance of informing us about it yet, in that case send will
201         // throw an error
202         try {
203           if (self.usingBrowserWebSocket) {
204             // TypeError is thrown when passing the second argument on Safari
205             self.ws.send(data);
206           } else {
207             self.ws.send(data, opts);
208           }
209         } catch (e) {
210           debug('websocket closed before onclose event');
211         }
212
213         --total || done();
214       });
215     })(packets[i]);
216   }
217
218   function done () {
219     self.emit('flush');
220
221     // fake drain
222     // defer to next tick to allow Socket to clear writeBuffer
223     setTimeout(function () {
224       self.writable = true;
225       self.emit('drain');
226     }, 0);
227   }
228 };
229
230 /**
231  * Called upon close
232  *
233  * @api private
234  */
235
236 WS.prototype.onClose = function () {
237   Transport.prototype.onClose.call(this);
238 };
239
240 /**
241  * Closes socket.
242  *
243  * @api private
244  */
245
246 WS.prototype.doClose = function () {
247   if (typeof this.ws !== 'undefined') {
248     this.ws.close();
249   }
250 };
251
252 /**
253  * Generates uri for connection.
254  *
255  * @api private
256  */
257
258 WS.prototype.uri = function () {
259   var query = this.query || {};
260   var schema = this.secure ? 'wss' : 'ws';
261   var port = '';
262
263   // avoid port if default for schema
264   if (this.port && (('wss' === schema && Number(this.port) !== 443) ||
265     ('ws' === schema && Number(this.port) !== 80))) {
266     port = ':' + this.port;
267   }
268
269   // append timestamp to URI
270   if (this.timestampRequests) {
271     query[this.timestampParam] = yeast();
272   }
273
274   // communicate binary support capabilities
275   if (!this.supportsBinary) {
276     query.b64 = 1;
277   }
278
279   query = parseqs.encode(query);
280
281   // prepend ? to query
282   if (query.length) {
283     query = '?' + query;
284   }
285
286   var ipv6 = this.hostname.indexOf(':') !== -1;
287   return schema + '://' + (ipv6 ? '[' + this.hostname + ']' : this.hostname) + port + this.path + query;
288 };
289
290 /**
291  * Feature detection for WebSocket.
292  *
293  * @return {Boolean} whether this transport is available.
294  * @api public
295  */
296
297 WS.prototype.check = function () {
298   return !!WebSocketImpl && !('__initialize' in WebSocketImpl && this.name === WS.prototype.name);
299 };