Allow static resources to be configured as online only.

Add a route policy type "static-online" for files in public/ that
shouldn't be cached offline.

Put "static-online" files in the manifest NETWORK section instead of
in the CACHE section.
This commit is contained in:
Andrew Wilcox
2013-02-20 14:21:47 +00:00
committed by Nick Martin
parent 1be9a4989c
commit 2500312cd5
4 changed files with 67 additions and 16 deletions

View File

@@ -157,9 +157,9 @@ var appUrl = function (url) {
if (url === '/app.manifest')
return false;
// Avoid serving app HTML for declared network routes such as /sockjs/.
// Avoid serving app HTML for declared routes such as /sockjs/.
if (__meteor_bootstrap__._routePolicy &&
__meteor_bootstrap__._routePolicy.classify(url) === 'network')
__meteor_bootstrap__._routePolicy.classify(url))
return false;
// we currently return app HTML on all URLs by default

View File

@@ -27,6 +27,11 @@
else if (_.contains(knownBrowsers, option)) {
enabledBrowsers[option] = value;
}
else if (option === 'onlineOnly') {
_.each(value, function (urlPrefix) {
Meteor._routePolicy.declare(urlPrefix, 'static-online');
});
}
else {
throw new Error('Invalid AppCache config option: ' + option)
}
@@ -93,7 +98,8 @@
manifest += "CACHE:" + "\n";
manifest += "/" + "\n";
_.each(bundle.manifest, function (resource) {
if (resource.where === 'client') {
if (resource.where === 'client' &&
! Meteor._routePolicy.classify(resource.url)) {
manifest += resource.url + "\n";
}
});
@@ -107,9 +113,15 @@
// TODO adding the manifest file to NETWORK should be unnecessary?
// Want more testing to be sure.
manifest += "/app.manifest" + "\n";
_.each(Meteor._routePolicy.urlPrefixesFor('network'), function (urlPrefix) {
manifest += urlPrefix + "\n";
});
_.each(
[].concat(
Meteor._routePolicy.urlPrefixesFor('network'),
Meteor._routePolicy.urlPrefixesFor('static-online')
),
function (urlPrefix) {
manifest += urlPrefix + "\n";
}
);
manifest += "*" + "\n";
// content length needs to be based on bytes
@@ -139,7 +151,10 @@
"** online as well as making it not cacheable for offline use).\n" +
"**\n" +
"** To avoid this problem we recommend keeping the size of your static\n" +
"** application assets under 5MB."
"** application assets under 5MB.\n" +
"**\n" +
"** If you have some larger assets that you'd like to make online only,\n" +
"** you can do that with the AppCache "onlineOnly" config option."
);
}
};

View File

@@ -1,3 +1,25 @@
// In addition to listing specific files to be cached, the browser
// application cache manifest allows URLs to be designated as NETWORK
// (always fetched from the Internet) and FALLBACK (which we use to
// serve app HTML on arbitrary URLs).
//
// The limitation of the manifest file format is that the designations
// are by prefix only: if "/foo" is declared NETWORK then "/foobar"
// will also be treated as a network route.
//
// Meteor._routePolicy is a low-level API for declaring the route type
// of URL prefixes:
//
// "network": for network routes that should not conflict with static
// resources. (For example, if "/sockjs/" is a network route, we
// shouldn't have "/sockjs/red-sock.jpg" as a static resource).
//
// "static-online": for static resources which should not be cached in
// the app cache. This is implemented by also adding them to the
// NETWORK section (as otherwise the browser would receive app HTML
// for them because of the FALLBACK section), but static-online routes
// don't need to be checked for conflict with static resources.
(function () {
// The route policy is a singleton in a running application, but we
@@ -17,8 +39,8 @@
},
checkType: function (type) {
if (! _.contains(['network'], type))
return 'the route type must be "network"';
if (! _.contains(['network', 'static-online'], type))
return 'the route type must be "network" or "static-online"';
return null;
},
@@ -35,6 +57,8 @@
checkForConflictWithStatic: function (urlPrefix, type, _testManifest) {
var self = this;
if (type === 'static-online')
return null;
var manifest = _testManifest || __meteor_bootstrap__.bundle.manifest;
var conflict = _.find(manifest, function (resource) {
return (resource.type === 'static' &&

View File

@@ -2,9 +2,8 @@ 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');
policy.declare('/bigphoto.jpg', 'static-online');
policy.declare('/anotherphoto.png', 'static-online');
test.equal(policy.classify('/'), null);
test.equal(policy.classify('/foo'), null);
@@ -13,11 +12,14 @@ Tinytest.add("routepolicy", function (test) {
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.classify('/bigphoto.jpg'), 'static-online');
test.equal(policy.classify('/bigphoto.jpg.orig'), 'static-online');
test.equal(policy.urlPrefixesFor('network'), ['/sockjs/']);
// test.equal(policy.urlPrefixesFor('app'), ['/about', '/posts/']);
test.equal(
policy.urlPrefixesFor('static-online'),
['/anotherphoto.png', '/bigphoto.jpg']
);
});
Tinytest.add("routepolicy - static conflicts", function (test) {
@@ -26,9 +28,14 @@ Tinytest.add("routepolicy - static conflicts", function (test) {
"path": "static/sockjs/socks-are-comfy.jpg",
"type": "static",
"where": "client",
"cacheable": false,
"url": "/sockjs/socks-are-comfy.jpg"
},
{
"path": "static/bigphoto.jpg",
"type": "static",
"where": "client",
"url": "/bigphoto.jpg"
}
];
var policy = new Meteor.__RoutePolicyConstructor();
@@ -36,4 +43,9 @@ Tinytest.add("routepolicy - static conflicts", function (test) {
policy.checkForConflictWithStatic('/sockjs/', 'network', manifest),
"static resource /sockjs/socks-are-comfy.jpg conflicts with network route /sockjs/"
);
test.equal(
policy.checkForConflictWithStatic('/bigphoto.jpg', 'static-online', manifest),
null
);
});