Skip to content

Commit

Permalink
Merge remote-tracking branch 'remotes/origin/pr/679' into devel. Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
n1mmy committed Feb 14, 2013
2 parents 8a69e74 + f2d9792 commit 8896281
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 15 deletions.
48 changes: 33 additions & 15 deletions app/server/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,29 @@ var runtime_config = function (app_html) {
return app_html;
};

// Serve app HTML for this URL?
var appUrl = function (url) {
if (url === '/favicon.ico' || url === '/robots.txt')
return false;

// NOTE: app.manifest is not a web standard like favicon.ico and
// robots.txt. It is a file name we have chosen to use for HTML5
// appcache URLs. It is included here to prevent using an appcache
// then removing it from poisoning an app permanently. Eventually,
// once we have server side routing, this won't be needed as
// unknown URLs with return a 404 automatically.
if (url === '/app.manifest')
return false;

// Avoid serving app HTML for declared network routes such as /sockjs/.
if (__meteor_bootstrap__._routePolicy &&
__meteor_bootstrap__._routePolicy.classify(url) === 'network')
return false;

// we currently return app HTML on all URLs by default
return true;
}

var run = function () {
var bundle_dir = path.join(__dirname, '..');

Expand Down Expand Up @@ -131,21 +154,9 @@ var run = function () {

app_html = runtime_config(app_html);

app.use(function (req, res) {
// prevent these URLs from returning app_html
//
// NOTE: app.manifest is not a web standard like favicon.ico and
// robots.txt. It is a file name we have chosen to use for HTML5
// appcache URLs. It is included here to prevent using an appcache
// then removing it from poisoning an app permanently. Eventually,
// once we have server side routing, this won't be needed as
// unknown URLs with return a 404 automatically.
if (_.indexOf(['/app.manifest', '/favicon.ico', '/robots.txt'], req.url)
!== -1) {
res.writeHead(404);
res.end();
return;
}
app.use(function (req, res, next) {
if (! appUrl(req.url))
return next();

res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'});
if (supported_browser(req.headers['user-agent']))
Expand All @@ -155,6 +166,13 @@ var run = function () {
res.end();
});

// Return 404 by default, if no other handlers serve this URL.
app.use(function (req, res) {
res.writeHead(404);
res.end();
return;
});

// run the user startup hooks.
_.each(__meteor_bootstrap__.startup_hooks, function (x) { x(); });

Expand Down
2 changes: 2 additions & 0 deletions packages/accounts-oauth-helper/oauth_server.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
(function () {
var connect = __meteor_bootstrap__.require("connect");

Meteor._routePolicy.declare('/_oauth/', 'network');

Accounts.oauth._services = {};

// Register a handler for an OAuth service. The handler will be called
Expand Down
1 change: 1 addition & 0 deletions packages/accounts-oauth-helper/package.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Package.describe({

Package.on_use(function (api) {
api.use('accounts-base', ['client', 'server']);
api.use('routepolicy', 'server');

api.add_files('oauth_common.js', ['client', 'server']);
api.add_files('oauth_client.js', 'client');
Expand Down
12 changes: 12 additions & 0 deletions packages/routepolicy/package.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Package.describe({
summary: "route policy declarations",
internal: true
});

Package.on_use(function (api) {
api.add_files('routepolicy.js', 'server');
});

Package.on_test(function (api) {
api.add_files(['routepolicy_tests.js'], 'server');
});
89 changes: 89 additions & 0 deletions packages/routepolicy/routepolicy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
(function () {

// The route policy is a singleton in a running application, but we
// can't unit test the real singleton because messing with the real
// routes would break tinytest... so allow policy instances to be
// constructed for testing.

Meteor.__RoutePolicyConstructor = function () {
var self = this;
self.urlPrefixTypes = {};
};

_.extend(Meteor.__RoutePolicyConstructor.prototype, {

urlPrefixMatches: function (urlPrefix, url) {
return url.substr(0, urlPrefix.length) === urlPrefix;
},

checkType: function (type) {
if (! _.contains(['network'], type))
return 'the route type must be "network"';
return null;
},

checkUrlPrefix: function (urlPrefix) {
var self = this;
if (urlPrefix.charAt(0) !== '/')
return 'a route URL prefix must begin with a slash';
if (urlPrefix === '/')
return 'a route URL prefix cannot be /';
if (self.urlPrefixTypes[urlPrefix] && self.urlPrefixTypes[urlPrefix] !== type)
return 'the route URL prefix ' + urlPrefix + ' has already been declared to be of type ' + type;
return null;
},

checkForConflictWithStatic: function (urlPrefix, type, _testManifest) {
var self = this;
var manifest = _testManifest || __meteor_bootstrap__.bundle.manifest;
var conflict = _.find(manifest, function (resource) {
return (resource.type === 'static' &&
resource.where === 'client' &&
self.urlPrefixMatches(urlPrefix, resource.url));
});
if (conflict)
return ('static resource ' + conflict.url + ' conflicts with ' +
type + ' route ' + urlPrefix);
else
return null;
},

declare: function (urlPrefix, type) {
var self = this;
var problem = self.checkType(type) ||
self.checkUrlPrefix(urlPrefix) ||
self.checkForConflictWithStatic(urlPrefix, type);
if (problem)
throw new Error(problem);
// TODO overlapping prefixes, e.g. /foo/ and /foo/bar/
self.urlPrefixTypes[urlPrefix] = type;
},

classify: function (url) {
var self = this;
if (url.charAt(0) !== '/')
throw new Error('url must be a relative URL: ' + url);
var prefix = _.find(_.keys(self.urlPrefixTypes), function (_prefix) {
return self.urlPrefixMatches(_prefix, url);
});
if (prefix)
return self.urlPrefixTypes[prefix];
else
return null;
},

urlPrefixesFor: function (type) {
var self = this;
var prefixes = [];
_.each(self.urlPrefixTypes, function (_type, _prefix) {
if (_type === type)
prefixes.push(_prefix);
});
return prefixes.sort();
}
});

__meteor_bootstrap__._routePolicy = Meteor._routePolicy =
new Meteor.__RoutePolicyConstructor();

})();
39 changes: 39 additions & 0 deletions packages/routepolicy/routepolicy_tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
Tinytest.add("routepolicy", function (test) {
var policy = new Meteor.__RoutePolicyConstructor();

policy.declare('/sockjs/', 'network');
// App routes might look like this...
// policy.declare('/posts/', 'app');
// policy.declare('/about', 'app');

test.equal(policy.classify('/'), null);
test.equal(policy.classify('/foo'), null);
test.equal(policy.classify('/sockjs'), null);

test.equal(policy.classify('/sockjs/'), 'network');
test.equal(policy.classify('/sockjs/foo'), 'network');

// test.equal(policy.classify('/posts/'), 'app');
// test.equal(policy.classify('/posts/1234'), 'app');

test.equal(policy.urlPrefixesFor('network'), ['/sockjs/']);
// test.equal(policy.urlPrefixesFor('app'), ['/about', '/posts/']);
});

Tinytest.add("routepolicy - static conflicts", function (test) {
var manifest = [
{
"path": "static/sockjs/socks-are-comfy.jpg",
"type": "static",
"where": "client",
"cacheable": false,
"url": "/sockjs/socks-are-comfy.jpg"
},
];
var policy = new Meteor.__RoutePolicyConstructor();

test.equal(
policy.checkForConflictWithStatic('/sockjs/', 'network', manifest),
"static resource /sockjs/socks-are-comfy.jpg conflicts with network route /sockjs/"
);
});
1 change: 1 addition & 0 deletions packages/stream/package.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Package.describe({
Package.on_use(function (api) {
api.use(['underscore', 'logging', 'uuid', 'json'], ['client', 'server']);
api.use('reload', 'client');
api.use('routepolicy', 'server');

api.add_files('sockjs-0.3.4.js', 'client');

Expand Down
2 changes: 2 additions & 0 deletions packages/stream/stream_server.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
Meteor._routePolicy.declare('/sockjs/', 'network');

// unique id for this instantiation of the server. If this changes
// between client reconnects, the client will reload. You can set the
// environment variable "SERVER_ID" to control this. For example, if
Expand Down

0 comments on commit 8896281

Please sign in to comment.