Share URL-munging code

Still to do:

* Share more code
* Deal with this error:
    Uncaught Error: INVALID_STATE_ERR sockjs-0.3.4.js:1054
    SockJS._didClose sockjs-0.3.4.js:1054
    (anonymous function) sockjs-0.3.4.js:1330
    xo.onfinish sockjs-0.3.4.js:1412
    EventEmitter.emit sockjs-0.3.4.js:150
    that.xhr.onreadystatechange
This commit is contained in:
Naomi Seyfer
2013-03-29 18:24:03 -07:00
parent 0fbb20e9be
commit e2ac47b451
5 changed files with 78 additions and 62 deletions

View File

@@ -16,6 +16,7 @@ Package.on_use(function (api) {
api.add_files(['sockjs-0.3.4.js',
'stream_client_sockjs.js'], 'client');
api.add_files('stream_client_nodejs.js', 'server');
api.add_files('urls.js', ['client', 'server']);
api.add_files('stream_server.js', 'server');
// livedata_connection.js uses a Minimongo collection internally to

View File

@@ -27,6 +27,11 @@ Meteor._DdpClientStream = function (endpoint) {
return self._onConnect(connection);
});
self.client.on('connectFailed', function (error) {
// XXX: Make this do something better than make the tests hang if it does not work.
return self._lostConnection();
});
//// Constants
// how long to wait until we declare the connection attempt
@@ -69,16 +74,6 @@ Meteor._DdpClientStream = function (endpoint) {
self._launchConnection();
};
_.extend(Meteor._DdpClientStream, {
_endpointToUrl: function (endpoint) {
// XXX should be secure!
// among other problems
endpoint = endpoint.replace(/^http(s)?:\/\//, "");
endpoint = endpoint.replace(/\/$/, "");
return 'ws://' + endpoint + '/websocket';
}
});
_.extend(Meteor._DdpClientStream.prototype, {
// Register for callbacks.
on: function (name, callback) {
@@ -303,7 +298,7 @@ _.extend(Meteor._DdpClientStream.prototype, {
// a protocol and the server doesn't send one back (and sockjs
// doesn't). also, related: I guess we have to accept that
// 'stream' is ddp-specific
self.client.connect(Meteor._DdpClientStream._endpointToUrl(self.endpoint));
self.client.connect(Meteor._DdpClientStream._toWebsocketUrl(self.endpoint));
if (self.connectionTimer)
clearTimeout(self.connectionTimer);

View File

@@ -62,56 +62,6 @@ Meteor._DdpClientStream = function (url) {
self._launch_connection();
};
_.extend(Meteor._DdpClientStream, {
// @param url {String} URL to Meteor app, eg:
// "/" or "madewith.meteor.com" or "https://foo.meteor.com"
// or "ddp+sockjs://ddp--****-foo.meteor.com/sockjs"
// @returns {String} URL to the sockjs endpoint, e.g.
// "http://subdomain.meteor.com/sockjs" or "/sockjs"
// or "https://ddp--1234-foo.meteor.com/sockjs"
_toSockjsUrl: function(url) {
// XXX from Underscore.String (http://epeli.github.com/underscore.string/)
var startsWith = function(str, starts) {
return str.length >= starts.length &&
str.substring(0, starts.length) === starts;
};
var endsWith = function(str, ends) {
return str.length >= ends.length &&
str.substring(str.length - ends.length) === ends;
};
var ddpUrlMatch = url.match(/^ddp(i?)\+sockjs:\/\//);
if (ddpUrlMatch) {
// Remove scheme and split off the host.
var urlAfterDDP = url.substr(ddpUrlMatch[0].length);
var newScheme = ddpUrlMatch[1] === 'i' ? 'http' : 'https';
var slashPos = urlAfterDDP.indexOf('/');
var host =
slashPos === -1 ? urlAfterDDP : urlAfterDDP.substr(0, slashPos);
var rest = slashPos === -1 ? '' : urlAfterDDP.substr(slashPos);
// In the host (ONLY!), change '*' characters into random digits. This
// allows different stream connections to connect to different hostnames
// and avoid browser per-hostname connection limits.
host = host.replace(/\*/g, function () {
return Math.floor(Random.fraction()*10);
});
return newScheme + '://' + host + rest;
}
// Prefix FQDNs but not relative URLs
if (url.indexOf("://") === -1 && !startsWith(url, "/")) {
url = "http://" + url;
}
if (endsWith(url, "/"))
return url + "sockjs";
else
return url + "/sockjs";
}
});
_.extend(Meteor._DdpClientStream.prototype, {
// Register for callbacks.
on: function (name, callback) {

View File

@@ -37,7 +37,7 @@ Tinytest.add("stream - sockjs urls are computed correctly", function(test) {
var testHasSockjsUrl = function(raw, expectedSockjsUrl) {
var actual = Meteor._DdpClientStream._toSockjsUrl(raw);
if (expectedSockjsUrl instanceof RegExp)
test.isTrue(actual.match(expectedSockjsUrl));
test.isTrue(actual.match(expectedSockjsUrl), actual);
else
test.equal(actual, expectedSockjsUrl);
};

70
packages/livedata/urls.js Normal file
View File

@@ -0,0 +1,70 @@
// XXX from Underscore.String (http://epeli.github.com/underscore.string/)
var startsWith = function(str, starts) {
return str.length >= starts.length &&
str.substring(0, starts.length) === starts;
};
var endsWith = function(str, ends) {
return str.length >= ends.length &&
str.substring(str.length - ends.length) === ends;
};
// @param url {String} URL to Meteor app, eg:
// "/" or "madewith.meteor.com" or "https://foo.meteor.com"
// or "ddp+sockjs://ddp--****-foo.meteor.com/sockjs"
// @returns {String} URL to the endpoint with the specific scheme and subPath, e.g.
// for scheme "http" and subPath "sockjs"
// "http://subdomain.meteor.com/sockjs" or "/sockjs"
// or "https://ddp--1234-foo.meteor.com/sockjs"
var translateUrl = function(url, newSchemeBase, subPath) {
if (! newSchemeBase) {
newSchemeBase = "http";
}
var ddpUrlMatch = url.match(/^ddp(i?)\+sockjs:\/\//);
var httpUrlMatch = url.match(/^http(s?):\/\//);
var newScheme;
if (ddpUrlMatch) {
// Remove scheme and split off the host.
var urlAfterDDP = url.substr(ddpUrlMatch[0].length);
newScheme = ddpUrlMatch[1] === "i" ? newSchemeBase : newSchemeBase + "s";
var slashPos = urlAfterDDP.indexOf('/');
var host =
slashPos === -1 ? urlAfterDDP : urlAfterDDP.substr(0, slashPos);
var rest = slashPos === -1 ? '' : urlAfterDDP.substr(slashPos);
// In the host (ONLY!), change '*' characters into random digits. This
// allows different stream connections to connect to different hostnames
// and avoid browser per-hostname connection limits.
host = host.replace(/\*/g, function () {
return Math.floor(Random.fraction()*10);
});
return newScheme + '://' + host + rest;
} else if (httpUrlMatch) {
newScheme = !httpUrlMatch[1] ? newSchemeBase : newSchemeBase + "s";
var urlAfterHttp = url.substr(httpUrlMatch[0].length);
url = newScheme + "://" + urlAfterHttp;
}
// Prefix FQDNs but not relative URLs
if (url.indexOf("://") === -1 && !startsWith(url, "/")) {
url = newSchemeBase + "://" + url;
}
if (endsWith(url, "/"))
return url + subPath;
else
return url + "/" + subPath;
};
_.extend(Meteor._DdpClientStream, {
_toSockjsUrl: function (url) {
return translateUrl(url, "http", "sockjs");
},
_toWebsocketUrl: function (url) {
var ret = translateUrl(url, "ws", "websocket");
return ret;
}
});