From a4f636472b7b23d180903a46d8c94c5eb1cb8140 Mon Sep 17 00:00:00 2001 From: Avi Stramer Date: Sat, 4 Jul 2020 10:29:11 -0500 Subject: [PATCH 1/5] Add support for passing options to transports other than websockets. Useful if you need to pass headers for example to an XHR request --- lib/info-ajax.js | 4 ++-- lib/info-iframe-receiver.js | 4 ++-- lib/info-receiver.js | 18 +++++++++--------- lib/main.js | 2 +- lib/transport/eventsource.js | 4 ++-- lib/transport/htmlfile.js | 4 ++-- lib/transport/lib/ajax-based.js | 13 +++++++------ lib/transport/lib/polling.js | 5 +++-- lib/transport/lib/sender-receiver.js | 4 ++-- lib/transport/receiver/xhr.js | 5 +++-- lib/transport/sender/xhr-local.js | 8 ++++---- lib/transport/xdr-polling.js | 4 ++-- lib/transport/xdr-streaming.js | 4 ++-- lib/transport/xhr-polling.js | 4 ++-- lib/transport/xhr-streaming.js | 4 ++-- package.json | 2 +- 16 files changed, 46 insertions(+), 43 deletions(-) diff --git a/lib/info-ajax.js b/lib/info-ajax.js index dda9ac96..77adcd78 100644 --- a/lib/info-ajax.js +++ b/lib/info-ajax.js @@ -11,12 +11,12 @@ if (process.env.NODE_ENV !== 'production') { debug = require('debug')('sockjs-client:info-ajax'); } -function InfoAjax(url, AjaxObject) { +function InfoAjax(url, AjaxObject, opts) { EventEmitter.call(this); var self = this; var t0 = +new Date(); - this.xo = new AjaxObject('GET', url); + this.xo = new AjaxObject('GET', url, null, opts); this.xo.once('finish', function(status, text) { var info, rtt; diff --git a/lib/info-iframe-receiver.js b/lib/info-iframe-receiver.js index cf529cf0..fc08deae 100644 --- a/lib/info-iframe-receiver.js +++ b/lib/info-iframe-receiver.js @@ -7,11 +7,11 @@ var inherits = require('inherits') , InfoAjax = require('./info-ajax') ; -function InfoReceiverIframe(transUrl) { +function InfoReceiverIframe(transUrl, ignore, opts) { var self = this; EventEmitter.call(this); - this.ir = new InfoAjax(transUrl, XHRLocalObject); + this.ir = new InfoAjax(transUrl, XHRLocalObject, opts); this.ir.once('finish', function(info, rtt) { self.ir = null; self.emit('message', JSON3.stringify([info, rtt])); diff --git a/lib/info-receiver.js b/lib/info-receiver.js index afefea5c..24f893c0 100644 --- a/lib/info-receiver.js +++ b/lib/info-receiver.js @@ -16,13 +16,13 @@ if (process.env.NODE_ENV !== 'production') { debug = require('debug')('sockjs-client:info-receiver'); } -function InfoReceiver(baseUrl, urlInfo) { +function InfoReceiver(baseUrl, urlInfo, transportOptions) { debug(baseUrl); var self = this; EventEmitter.call(this); setTimeout(function() { - self.doXhr(baseUrl, urlInfo); + self.doXhr(baseUrl, urlInfo, transportOptions); }, 0); } @@ -30,30 +30,30 @@ inherits(InfoReceiver, EventEmitter); // TODO this is currently ignoring the list of available transports and the whitelist -InfoReceiver._getReceiver = function(baseUrl, url, urlInfo) { +InfoReceiver._getReceiver = function(baseUrl, url, urlInfo, transportOptions) { // determine method of CORS support (if needed) if (urlInfo.sameOrigin) { - return new InfoAjax(url, XHRLocal); + return new InfoAjax(url, XHRLocal, transportOptions['xhr-streaming'] || transportOptions['xhr-polling']); } if (XHRCors.enabled) { - return new InfoAjax(url, XHRCors); + return new InfoAjax(url, XHRCors, transportOptions['xhr-streaming'] || transportOptions['xhr-polling']); } if (XDR.enabled && urlInfo.sameScheme) { - return new InfoAjax(url, XDR); + return new InfoAjax(url, XDR, transportOptions['xdr-streaming'] || transportOptions['xdr-polling']); } if (InfoIframe.enabled()) { - return new InfoIframe(baseUrl, url); + return new InfoIframe(baseUrl, url, transportOptions['iframe']); } return new InfoAjax(url, XHRFake); }; -InfoReceiver.prototype.doXhr = function(baseUrl, urlInfo) { +InfoReceiver.prototype.doXhr = function(baseUrl, urlInfo, transportOptions) { var self = this , url = urlUtils.addPath(baseUrl, '/info') ; debug('doXhr', url); - this.xo = InfoReceiver._getReceiver(baseUrl, url, urlInfo); + this.xo = InfoReceiver._getReceiver(baseUrl, url, urlInfo, transportOptions); this.timeoutRef = setTimeout(function() { debug('timeout'); diff --git a/lib/main.js b/lib/main.js index eb669bee..b97c9c5d 100644 --- a/lib/main.js +++ b/lib/main.js @@ -122,7 +122,7 @@ function SockJS(url, protocols, options) { , sameScheme: urlUtils.isSchemeEqual(this.url, loc.href) }; - this._ir = new InfoReceiver(this.url, this._urlInfo); + this._ir = new InfoReceiver(this.url, this._urlInfo, this._transportOptions); this._ir.once('finish', this._receiveInfo.bind(this)); } diff --git a/lib/transport/eventsource.js b/lib/transport/eventsource.js index 62685cfd..3a355c88 100644 --- a/lib/transport/eventsource.js +++ b/lib/transport/eventsource.js @@ -7,12 +7,12 @@ var inherits = require('inherits') , EventSourceDriver = require('eventsource') ; -function EventSourceTransport(transUrl) { +function EventSourceTransport(transUrl, ignore, opts) { if (!EventSourceTransport.enabled()) { throw new Error('Transport created when disabled'); } - AjaxBasedTransport.call(this, transUrl, '/eventsource', EventSourceReceiver, XHRCorsObject); + AjaxBasedTransport.call(this, transUrl, '/eventsource', EventSourceReceiver, XHRCorsObject, opts); } inherits(EventSourceTransport, AjaxBasedTransport); diff --git a/lib/transport/htmlfile.js b/lib/transport/htmlfile.js index 633b0774..10efada3 100644 --- a/lib/transport/htmlfile.js +++ b/lib/transport/htmlfile.js @@ -6,11 +6,11 @@ var inherits = require('inherits') , AjaxBasedTransport = require('./lib/ajax-based') ; -function HtmlFileTransport(transUrl) { +function HtmlFileTransport(transUrl, ignore, opts) { if (!HtmlfileReceiver.enabled) { throw new Error('Transport created when disabled'); } - AjaxBasedTransport.call(this, transUrl, '/htmlfile', HtmlfileReceiver, XHRLocalObject); + AjaxBasedTransport.call(this, transUrl, '/htmlfile', HtmlfileReceiver, XHRLocalObject, opts); } inherits(HtmlFileTransport, AjaxBasedTransport); diff --git a/lib/transport/lib/ajax-based.js b/lib/transport/lib/ajax-based.js index 29e104e7..0141dee3 100644 --- a/lib/transport/lib/ajax-based.js +++ b/lib/transport/lib/ajax-based.js @@ -10,15 +10,16 @@ if (process.env.NODE_ENV !== 'production') { debug = require('debug')('sockjs-client:ajax-based'); } -function createAjaxSender(AjaxObject) { +function createAjaxSender(AjaxObject, opts) { return function(url, payload, callback) { debug('create ajax sender', url, payload); - var opt = {}; + opts = opts || {}; if (typeof payload === 'string') { - opt.headers = {'Content-type': 'text/plain'}; + opts.headers = opts.headers || {}; + opts.headers['Content-type'] = 'text/plain'; } var ajaxUrl = urlUtils.addPath(url, '/xhr_send'); - var xo = new AjaxObject('POST', ajaxUrl, payload, opt); + var xo = new AjaxObject('POST', ajaxUrl, payload, opts); xo.once('finish', function(status) { debug('finish', status); xo = null; @@ -40,8 +41,8 @@ function createAjaxSender(AjaxObject) { }; } -function AjaxBasedTransport(transUrl, urlSuffix, Receiver, AjaxObject) { - SenderReceiver.call(this, transUrl, urlSuffix, createAjaxSender(AjaxObject), Receiver, AjaxObject); +function AjaxBasedTransport(transUrl, urlSuffix, Receiver, AjaxObject, opts) { + SenderReceiver.call(this, transUrl, urlSuffix, createAjaxSender(AjaxObject, opts), Receiver, AjaxObject, opts); } inherits(AjaxBasedTransport, SenderReceiver); diff --git a/lib/transport/lib/polling.js b/lib/transport/lib/polling.js index 8108432b..e966207a 100644 --- a/lib/transport/lib/polling.js +++ b/lib/transport/lib/polling.js @@ -9,12 +9,13 @@ if (process.env.NODE_ENV !== 'production') { debug = require('debug')('sockjs-client:polling'); } -function Polling(Receiver, receiveUrl, AjaxObject) { +function Polling(Receiver, receiveUrl, AjaxObject, opts) { debug(receiveUrl); EventEmitter.call(this); this.Receiver = Receiver; this.receiveUrl = receiveUrl; this.AjaxObject = AjaxObject; + this.opts = opts; this._scheduleReceiver(); } @@ -23,7 +24,7 @@ inherits(Polling, EventEmitter); Polling.prototype._scheduleReceiver = function() { debug('_scheduleReceiver'); var self = this; - var poll = this.poll = new this.Receiver(this.receiveUrl, this.AjaxObject); + var poll = this.poll = new this.Receiver(this.receiveUrl, this.AjaxObject, this.opts); poll.on('message', function(msg) { debug('message', msg); diff --git a/lib/transport/lib/sender-receiver.js b/lib/transport/lib/sender-receiver.js index 8154994c..075157ed 100644 --- a/lib/transport/lib/sender-receiver.js +++ b/lib/transport/lib/sender-receiver.js @@ -11,13 +11,13 @@ if (process.env.NODE_ENV !== 'production') { debug = require('debug')('sockjs-client:sender-receiver'); } -function SenderReceiver(transUrl, urlSuffix, senderFunc, Receiver, AjaxObject) { +function SenderReceiver(transUrl, urlSuffix, senderFunc, Receiver, AjaxObject, opts) { var pollUrl = urlUtils.addPath(transUrl, urlSuffix); debug(pollUrl); var self = this; BufferedSender.call(this, transUrl, senderFunc); - this.poll = new Polling(Receiver, pollUrl, AjaxObject); + this.poll = new Polling(Receiver, pollUrl, AjaxObject, opts); this.poll.on('message', function(msg) { debug('poll message', msg); self.emit('message', msg); diff --git a/lib/transport/receiver/xhr.js b/lib/transport/receiver/xhr.js index 8ec7bc84..4bd00bd8 100644 --- a/lib/transport/receiver/xhr.js +++ b/lib/transport/receiver/xhr.js @@ -9,14 +9,15 @@ if (process.env.NODE_ENV !== 'production') { debug = require('debug')('sockjs-client:receiver:xhr'); } -function XhrReceiver(url, AjaxObject) { +function XhrReceiver(url, AjaxObject, opts) { debug(url); EventEmitter.call(this); var self = this; this.bufferPosition = 0; - this.xo = new AjaxObject('POST', url, null); + this.opts = opts; + this.xo = new AjaxObject('POST', url, null, opts); this.xo.on('chunk', this._chunkHandler.bind(this)); this.xo.once('finish', function(status, text) { debug('finish', status, text); diff --git a/lib/transport/sender/xhr-local.js b/lib/transport/sender/xhr-local.js index defe4d25..1f71e675 100644 --- a/lib/transport/sender/xhr-local.js +++ b/lib/transport/sender/xhr-local.js @@ -4,10 +4,10 @@ var inherits = require('inherits') , XhrDriver = require('../driver/xhr') ; -function XHRLocalObject(method, url, payload /*, opts */) { - XhrDriver.call(this, method, url, payload, { - noCredentials: true - }); +function XHRLocalObject(method, url, payload, opts) { + opts = opts || {}; + opts.noCredentials = true; + XhrDriver.call(this, method, url, payload, opts); } inherits(XHRLocalObject, XhrDriver); diff --git a/lib/transport/xdr-polling.js b/lib/transport/xdr-polling.js index da84404d..3d79c9f4 100644 --- a/lib/transport/xdr-polling.js +++ b/lib/transport/xdr-polling.js @@ -7,11 +7,11 @@ var inherits = require('inherits') , XDRObject = require('./sender/xdr') ; -function XdrPollingTransport(transUrl) { +function XdrPollingTransport(transUrl, ignore, opts) { if (!XDRObject.enabled) { throw new Error('Transport created when disabled'); } - AjaxBasedTransport.call(this, transUrl, '/xhr', XhrReceiver, XDRObject); + AjaxBasedTransport.call(this, transUrl, '/xhr', XhrReceiver, XDRObject, opts); } inherits(XdrPollingTransport, AjaxBasedTransport); diff --git a/lib/transport/xdr-streaming.js b/lib/transport/xdr-streaming.js index 0fede496..3ff46c38 100644 --- a/lib/transport/xdr-streaming.js +++ b/lib/transport/xdr-streaming.js @@ -10,11 +10,11 @@ var inherits = require('inherits') // http://stackoverflow.com/questions/1641507/detect-browser-support-for-cross-domain-xmlhttprequests // http://hacks.mozilla.org/2009/07/cross-site-xmlhttprequest-with-cors/ -function XdrStreamingTransport(transUrl) { +function XdrStreamingTransport(transUrl, ignore, opts) { if (!XDRObject.enabled) { throw new Error('Transport created when disabled'); } - AjaxBasedTransport.call(this, transUrl, '/xhr_streaming', XhrReceiver, XDRObject); + AjaxBasedTransport.call(this, transUrl, '/xhr_streaming', XhrReceiver, XDRObject, opts); } inherits(XdrStreamingTransport, AjaxBasedTransport); diff --git a/lib/transport/xhr-polling.js b/lib/transport/xhr-polling.js index a07ad9f1..cc9e00ec 100644 --- a/lib/transport/xhr-polling.js +++ b/lib/transport/xhr-polling.js @@ -7,11 +7,11 @@ var inherits = require('inherits') , XHRLocalObject = require('./sender/xhr-local') ; -function XhrPollingTransport(transUrl) { +function XhrPollingTransport(transUrl, ignore, opts) { if (!XHRLocalObject.enabled && !XHRCorsObject.enabled) { throw new Error('Transport created when disabled'); } - AjaxBasedTransport.call(this, transUrl, '/xhr', XhrReceiver, XHRCorsObject); + AjaxBasedTransport.call(this, transUrl, '/xhr', XhrReceiver, XHRCorsObject, opts); } inherits(XhrPollingTransport, AjaxBasedTransport); diff --git a/lib/transport/xhr-streaming.js b/lib/transport/xhr-streaming.js index 4c3b54b8..05576f14 100644 --- a/lib/transport/xhr-streaming.js +++ b/lib/transport/xhr-streaming.js @@ -8,11 +8,11 @@ var inherits = require('inherits') , browser = require('../utils/browser') ; -function XhrStreamingTransport(transUrl) { +function XhrStreamingTransport(transUrl, ignore, opts) { if (!XHRLocalObject.enabled && !XHRCorsObject.enabled) { throw new Error('Transport created when disabled'); } - AjaxBasedTransport.call(this, transUrl, '/xhr_streaming', XhrReceiver, XHRCorsObject); + AjaxBasedTransport.call(this, transUrl, '/xhr_streaming', XhrReceiver, XHRCorsObject, opts); } inherits(XhrStreamingTransport, AjaxBasedTransport); diff --git a/package.json b/package.json index a4e6c66f..afcf0626 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "sockjs-client", + "name": "testable-sockjs-client", "description": "SockJS-client is a browser JavaScript library that provides a WebSocket-like object.", "version": "1.5.0", "author": "Bryce Kahle", From 51ed91bddf22a6bc284d067a4847018b4e940484 Mon Sep 17 00:00:00 2001 From: Avi Stramer Date: Sun, 5 Jul 2020 09:25:38 -0500 Subject: [PATCH 2/5] add oninfo event to main interface --- README.md | 5 +++++ lib/event/info.js | 17 +++++++++++++++++ lib/info-ajax.js | 4 ++-- lib/info-receiver.js | 6 +++--- lib/main.js | 4 +++- lib/transport/driver/xhr.js | 2 +- 6 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 lib/event/info.js diff --git a/README.md b/README.md index 6b9ca4f8..ee545b49 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,11 @@ Where `options` is a hash which can contain: the number of expected round trips. This setting will establish a minimum, but if the calculated timeout is higher, that will be used. + * **transportOptions (object)** + + Additional options to pass per transport, keyed on transport name. For example: + ```{ 'xhr-streaming': { headers: { Authorization: 'xxx' } } }``` + Although the 'SockJS' object tries to emulate the 'WebSocket' behaviour, it's impossible to support all of its features. An important SockJS limitation is the fact that you're not allowed to diff --git a/lib/event/info.js b/lib/event/info.js new file mode 100644 index 00000000..3b7aee82 --- /dev/null +++ b/lib/event/info.js @@ -0,0 +1,17 @@ +'use strict'; + +var inherits = require('inherits') + , Event = require('./event') + ; + +function InfoEvent(info, rtt, headers) { + Event.call(this); + this.initEvent('info', false, false); + this.info = info; + this.rtt = rtt; + this.headers = headers; +} + +inherits(InfoEvent, Event); + +module.exports = InfoEvent; diff --git a/lib/info-ajax.js b/lib/info-ajax.js index 77adcd78..4bb59c12 100644 --- a/lib/info-ajax.js +++ b/lib/info-ajax.js @@ -18,7 +18,7 @@ function InfoAjax(url, AjaxObject, opts) { var t0 = +new Date(); this.xo = new AjaxObject('GET', url, null, opts); - this.xo.once('finish', function(status, text) { + this.xo.once('finish', function(status, text, headers) { var info, rtt; if (status === 200) { rtt = (+new Date()) - t0; @@ -34,7 +34,7 @@ function InfoAjax(url, AjaxObject, opts) { info = {}; } } - self.emit('finish', info, rtt); + self.emit('finish', info, rtt, headers); self.removeAllListeners(); }); } diff --git a/lib/info-receiver.js b/lib/info-receiver.js index 24f893c0..468bd778 100644 --- a/lib/info-receiver.js +++ b/lib/info-receiver.js @@ -61,10 +61,10 @@ InfoReceiver.prototype.doXhr = function(baseUrl, urlInfo, transportOptions) { self.emit('finish'); }, InfoReceiver.timeout); - this.xo.once('finish', function(info, rtt) { - debug('finish', info, rtt); + this.xo.once('finish', function(info, rtt, headers) { + debug('finish', info, rtt, headers); self._cleanup(true); - self.emit('finish', info, rtt); + self.emit('finish', info, rtt, headers || {}); }); }; diff --git a/lib/main.js b/lib/main.js index b97c9c5d..d27d7462 100644 --- a/lib/main.js +++ b/lib/main.js @@ -18,6 +18,7 @@ var URL = require('url-parse') , loc = require('./location') , CloseEvent = require('./event/close') , TransportMessageEvent = require('./event/trans-message') + , InfoEvent = require('./event/info') , InfoReceiver = require('./info-receiver') ; @@ -174,8 +175,9 @@ SockJS.OPEN = 1; SockJS.CLOSING = 2; SockJS.CLOSED = 3; -SockJS.prototype._receiveInfo = function(info, rtt) { +SockJS.prototype._receiveInfo = function(info, rtt, headers) { debug('_receiveInfo', rtt); + this.dispatchEvent(new InfoEvent(info, rtt, headers)); this._ir = null; if (!info) { this._close(1002, 'Cannot connect to server'); diff --git a/lib/transport/driver/xhr.js b/lib/transport/driver/xhr.js index ccf65fef..8e591fab 100644 --- a/lib/transport/driver/xhr.js +++ b/lib/transport/driver/xhr.js @@ -39,7 +39,7 @@ function XhrDriver(method, url, payload, opts) { }); res.once('end', function() { debug('end'); - self.emit('finish', res.statusCode, responseText); + self.emit('finish', res.statusCode, responseText, res.headers); self.req = null; }); }); From c594d4645b5276635a6e3c70bdb8a47dd54bbb24 Mon Sep 17 00:00:00 2001 From: Avi Stramer Date: Mon, 1 Mar 2021 15:14:27 -0600 Subject: [PATCH 3/5] revert package name --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index afcf0626..a4e6c66f 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "testable-sockjs-client", + "name": "sockjs-client", "description": "SockJS-client is a browser JavaScript library that provides a WebSocket-like object.", "version": "1.5.0", "author": "Bryce Kahle", From 8bff03f60e98326bdfcd68aca06f7802b03efe75 Mon Sep 17 00:00:00 2001 From: avistramer Date: Sun, 3 Nov 2024 19:30:33 -0600 Subject: [PATCH 4/5] add support for drain event and send response boolean to make sockjs compliant with Writeable api that allows for handling back pressure --- lib/main.js | 6 +++++- lib/transport/iframe.js | 1 + lib/transport/lib/buffered-sender.js | 1 + lib/transport/websocket.js | 5 ++++- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/main.js b/lib/main.js index 90bf56db..ab3c9973 100644 --- a/lib/main.js +++ b/lib/main.js @@ -164,7 +164,7 @@ SockJS.prototype.send = function(data) { if (this.readyState !== SockJS.OPEN) { return; } - this._transport.send(escape.quote(data)); + return this._transport.send(escape.quote(data)); }; SockJS.version = require('./version'); @@ -223,6 +223,10 @@ SockJS.prototype._connect = function() { debug('transport url', transportUrl); var transportObj = new Transport(transportUrl, this._transUrl, options); transportObj.on('message', this._transportMessage.bind(this)); + // for transports that support backpressure handle drain event + transportObj.on('drain', function() { + self.dispatchEvent(new Event('drain')); + }); transportObj.once('close', this._transportClose.bind(this)); transportObj.transportName = Transport.transportName; this._transport = transportObj; diff --git a/lib/transport/iframe.js b/lib/transport/iframe.js index d567339f..736a2948 100644 --- a/lib/transport/iframe.js +++ b/lib/transport/iframe.js @@ -128,6 +128,7 @@ IframeTransport.prototype.postMessage = function(type, data) { IframeTransport.prototype.send = function(message) { debug('send', message); this.postMessage('m', message); + return true; }; IframeTransport.enabled = function() { diff --git a/lib/transport/lib/buffered-sender.js b/lib/transport/lib/buffered-sender.js index dda9163f..e0b33602 100644 --- a/lib/transport/lib/buffered-sender.js +++ b/lib/transport/lib/buffered-sender.js @@ -25,6 +25,7 @@ BufferedSender.prototype.send = function(message) { if (!this.sendStop) { this.sendSchedule(); } + return true; }; // For polling transports in a situation when in the message callback, diff --git a/lib/transport/websocket.js b/lib/transport/websocket.js index b092e286..37fc4b45 100644 --- a/lib/transport/websocket.js +++ b/lib/transport/websocket.js @@ -54,6 +54,9 @@ function WebSocketTransport(transUrl, ignore, options) { self.emit('close', 1006, 'WebSocket connection broken'); self._cleanup(); }; + this.ws.on('drain', function() { + self.emit('drain'); + }) } inherits(WebSocketTransport, EventEmitter); @@ -61,7 +64,7 @@ inherits(WebSocketTransport, EventEmitter); WebSocketTransport.prototype.send = function(data) { var msg = '[' + data + ']'; debug('send', msg); - this.ws.send(msg); + return this.ws.send(msg); }; WebSocketTransport.prototype.close = function() { From 093829d8b0e4a0158f2a773ef1aab3ced21c937d Mon Sep 17 00:00:00 2001 From: avistramer Date: Tue, 12 Nov 2024 11:43:54 -0600 Subject: [PATCH 5/5] missin self reference --- lib/main.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/main.js b/lib/main.js index ab3c9973..e28f5043 100644 --- a/lib/main.js +++ b/lib/main.js @@ -199,6 +199,7 @@ SockJS.prototype._receiveInfo = function(info, rtt, status, headers) { }; SockJS.prototype._connect = function() { + var self = this; for (var Transport = this._transports.shift(); Transport; Transport = this._transports.shift()) { debug('attempt', Transport.transportName); if (Transport.needBody) {