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