Skip to content

Commit

Permalink
Fix #49 - Support for sockjs-client being used from pages served from…
Browse files Browse the repository at this point in the history
… file:// addresses
  • Loading branch information
majek committed Mar 6, 2012
1 parent ca7b838 commit 88375e0
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 23 deletions.
25 changes: 17 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,8 @@ bad practice. If you absolutely must do it, you can use
mutliple subdomains, using different subdomain for every
SockJS connection.

Supported transports, by browser
--------------------------------
Supported transports, by browser (html served from http:// or https://)
-----------------------------------------------------------------------

_Browser_ | _Websockets_ | _Streaming_ | _Polling_
----------------|------------------|-------------|-----------
Expand All @@ -182,6 +182,21 @@ Konqueror | no | no | jsonp-polling
Websockets "hixie-76". They can still be enabled by manually
changing a browser setting.

Supported transports, by browser (html served from file://)
-----------------------------------------------------------

Sometimes you may want to serve your html from "file://" address - for
development or if you're using PhoneGap or similar technologies. But
due to the Cross Origin Policy files served from "file://" have no
Origin, and that means some of SockJS transports won't work. For this
reason the SockJS protocol table is different than usually, major
differences are:

_Browser_ | _Websockets_ | _Streaming_ | _Polling_
----------------|---------------|--------------------|-----------
IE 8, 9 | same as above | iframe-htmlfile | iframe-xhr-polling
Other | same as above | iframe-eventsource | iframe-xhr-polling

Supported transports, by name
-----------------------------

Expand Down Expand Up @@ -344,12 +359,6 @@ There are various browser quirks which we don't intend to address:
that have a proper Unicode support.
* Having a global function called `onmessage` or such is probably a
bad idea, as it could be called by the built-in `postMessage` API.
* Serving an html page that uses SockJS from `file://` url will not
work. This is due to a badly thought through
[CORS specification](http://dvcs.w3.org/hg/cors/raw-file/tip/Overview.html)
It is impossible to receive response to an Ajax request with
cookies set (`withCredentials` set to `true`) sent from a `file://`
origin.
* From SockJS point of view there is nothing special about
SSL/HTTPS. Connecting between unencrypted and encrypted sites
should work just fine.
Expand Down
13 changes: 7 additions & 6 deletions lib/dom2.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
var XHRObject = utils.XHRObject = function(method, url, payload) {
var that = this;
utils.delay(function(){that._start(method, url, payload);});
var XHRObject = utils.XHRObject = function() {
var that = this, args = arguments;
utils.delay(function(){that._start.apply(that, args);});
};

XHRObject.prototype = new EventEmitter(['chunk', 'finish']);

XHRObject.prototype._start = function(method, url, payload) {
XHRObject.prototype._start = function(method, url, payload, opts) {
var that = this;
try {
that.xhr = new _window.ActiveXObject('Microsoft.XMLHTTP');
Expand All @@ -28,7 +28,7 @@ XHRObject.prototype._start = function(method, url, payload) {
return;
};

if ('withCredentials' in that.xhr) {
if ('withCredentials' in that.xhr && (!opts || !opts.no_credentials)) {
that.xhr.withCredentials = 'true';
}

Expand Down Expand Up @@ -143,7 +143,8 @@ utils.isXHRCorsCapable = function() {
if (_window.XMLHttpRequest && 'withCredentials' in new XMLHttpRequest()) {
return 1;
}
if (_window.XDomainRequest) {
// XDomainRequest doesn't work if page is served from file://
if (_window.XDomainRequest && _document.domain) {
return 2;
}
if (IframeTransport.enabled()) {
Expand Down
3 changes: 2 additions & 1 deletion lib/info.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ InfoReceiver.prototype = new EventEmitter(['finish']);
InfoReceiver.prototype.doXhr = function(base_url, AjaxObject) {
var that = this;
var t0 = (new Date()).getTime();
var xo = new AjaxObject('GET', base_url + '/info' , null);
var xo = new AjaxObject('GET', base_url + '/info', null,
{no_credentials: true});

var tref = utils.delay(8000,
function(){xo.ontimeout();});
Expand Down
1 change: 1 addition & 0 deletions lib/sockjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ SockJS.prototype._applyInfo = function(info, rtt, protocols_whitelist) {
that._options.info = info;
that._options.rtt = rtt;
that._options.rto = utils.countRTO(rtt);
that._options.info.null_origin = !_document.domain;
var probed = utils.probeProtocols();
that._protocols = utils.detectProtocols(probed, protocols_whitelist, info);
};
22 changes: 14 additions & 8 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,21 +283,27 @@ utils.detectProtocols = function(probed, protocols_whitelist, info) {
}

// 2. Streaming
if (pe['xdr-streaming'] && !info.cookie_needed) {
if (pe['xdr-streaming'] && !info.cookie_needed && !info.null_origin) {
protocols.push('xdr-streaming');
} else {
maybe_push(['xhr-streaming',
'iframe-eventsource',
'iframe-htmlfile']);
if (pe['xhr-streaming'] && !info.null_origin) {
protocols.push('xhr-streaming');
} else {
maybe_push(['iframe-eventsource',
'iframe-htmlfile']);
}
}

// 3. Polling
if (pe['xdr-polling'] && !info.cookie_needed) {
if (pe['xdr-polling'] && !info.cookie_needed && !info.null_origin) {
protocols.push('xdr-polling');
} else {
maybe_push(['xhr-polling',
'iframe-xhr-polling',
'jsonp-polling']);
if (pe['xhr-polling'] && !info.null_origin) {
protocols.push('xhr-polling');
} else {
maybe_push(['iframe-xhr-polling',
'jsonp-polling']);
}
}
return protocols;
}
15 changes: 15 additions & 0 deletions tests/html/src/unittests.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,21 @@ test 'detectProtocols', ->
deepEqual(u.detectProtocols(ie8_probed, null, {cookie_needed:true}),
['iframe-htmlfile', 'iframe-xhr-polling'])

# Check if protocols are picked up correctly when served from file://
deepEqual(u.detectProtocols(chrome_probed, null, {null_origin:true}),
['websocket', 'iframe-eventsource', 'iframe-xhr-polling'])
deepEqual(u.detectProtocols(chrome_probed, null,
{websocket:false, null_origin:true}),
['iframe-eventsource', 'iframe-xhr-polling'])

deepEqual(u.detectProtocols(opera_probed, null, {null_origin:true}),
['iframe-eventsource', 'iframe-xhr-polling'])

deepEqual(u.detectProtocols(ie6_probed, null, {null_origin:true}),
['jsonp-polling'])
deepEqual(u.detectProtocols(ie8_probed, null, {null_origin:true}),
['iframe-htmlfile', 'iframe-xhr-polling'])

test "EventEmitter", ->
expect(4)
r = new SockJS('//wrongdomainthatdoesntresolveatall/abc', null,
Expand Down

0 comments on commit 88375e0

Please sign in to comment.