Use manifest, not directory serving, to serve static files.

This commit is contained in:
David Glasser
2013-07-08 23:28:15 -07:00
parent 34d563a7ed
commit 5b8e1c17f3
4 changed files with 82 additions and 41 deletions

View File

@@ -43,6 +43,23 @@
}
}
},
"send": {
"version": "0.1.0",
"dependencies": {
"debug": {
"version": "0.7.2"
},
"mime": {
"version": "1.2.6"
},
"fresh": {
"version": "0.1.0"
},
"range-parser": {
"version": "0.0.4"
}
}
},
"useragent": {
"version": "2.0.1"
}

View File

@@ -4,6 +4,7 @@ Package.describe({
});
Npm.depends({connect: "2.7.10",
send: "0.1.0",
useragent: "2.0.1"});
Package.on_use(function (api) {

View File

@@ -9,6 +9,7 @@ var url = Npm.require("url");
var connect = Npm.require('connect');
var optimist = Npm.require('optimist');
var useragent = Npm.require('useragent');
var send = Npm.require('send');
// @export WebApp
WebApp = {};
@@ -215,26 +216,68 @@ var runWebAppServer = function () {
// Auto-compress any json, javascript, or text.
app.use(connect.compress());
if (clientJson.staticCacheable) {
// cacheable files are files that should never change. Typically
// named by their hash (eg meteor bundled js and css files).
// cache them ~forever (1yr)
app.use(connect.static(path.join(clientDir, clientJson.staticCacheable),
{maxAge: 1000 * 60 * 60 * 24 * 365}));
}
var staticFiles = {};
_.each(clientJson.manifest, function (item) {
if (item.url && item.where === "client")
staticFiles[url.parse(item.url).pathname] = item;
});
// cache non-cacheable file anyway. This isn't really correct, as
// users can change the files and changes won't propogate
// immediately. However, if we don't cache them, browsers will
// 'flicker' when rerendering images. Eventually we will probably want
// to rewrite URLs of static assets to include a query parameter to
// bust caches. That way we can both get good caching behavior and
// allow users to change assets without delay.
// https://github.com/meteor/meteor/issues/773
if (clientJson.static) {
app.use(connect.static(path.join(clientDir, clientJson.static),
{maxAge: 1000 * 60 * 60 * 24}));
}
// Serve static files from the manifest.
// This is inspired by the 'static' middleware.
app.use(function (req, res, next) {
if ('GET' != req.method && 'HEAD' != req.method) {
next();
return;
}
var path = connect.utils.parseUrl(req).pathname;
try {
path = decodeURIComponent(path);
} catch (e) {
next();
return;
}
if (!_.has(staticFiles, path)) {
next();
return;
}
// We don't need to call pause because, unlike 'static', once we call into
// 'send' and yield to the event loop, we never call another handler with
// 'next'.
var info = staticFiles[path];
// Cacheable files are files that should never change. Typically
// named by their hash (eg meteor bundled js and css files).
// We cache them ~forever (1yr).
//
// We cache non-cacheable files anyway. This isn't really correct, as users
// can change the files and changes won't propagate immediately. However, if
// we don't cache them, browsers will 'flicker' when rerendering
// images. Eventually we will probably want to rewrite URLs of static assets
// to include a query parameter to bust caches. That way we can both get
// good caching behavior and allow users to change assets without delay.
// https://github.com/meteor/meteor/issues/773
var maxAge = info.cacheable
? 1000 * 60 * 60 * 24 * 365
: 1000 * 60 * 60 * 24;
send(req, path.join(clientDir, info.path))
.maxage(maxAge)
.hidden(true) // if we specified a dotfile in the manifest, serve it
.on('error', function (err) {
Log.error("Error serving static file " + err);
res.writeHead(500);
res.end();
})
.on('directory', function () {
Log.error("Unexpected directory " + info.path);
res.writeHead(500);
res.end();
})
.pipe(res);
});
// Packages and apps can add handlers to this via WebApp.connectHandlers.
// They are inserted before our default handler.

View File

@@ -76,20 +76,9 @@
// trigger HTML5 appcache reloads at the right time (if the
// 'appcache' package is being used.)
//
// - static: a path, relative to program.json, to a directory. If the
// server is too dumb to read 'manifest', it can just serve all of
// the files in this directory (with a relatively short cache
// expiry time.)
// XXX do not use this. It will go away soon.
//
// - static_cacheable: just like 'static' but resources that can be
// cached aggressively (cacheable: true in the manifest)
// XXX do not use this. It will go away soon.
//
// Convention:
//
// page is 'app.html', static is 'static', and staticCacheable is
// 'static_cacheable'.
// page is 'app.html'.
//
//
// == Format of a program when arch is "native.*" ==
@@ -914,17 +903,8 @@ _.extend(ClientTarget.prototype, {
// Control file
builder.writeJson('program.json', {
format: "browser-program-pre1",
manifest: manifest,
page: 'app.html',
// XXX the following are for use by 'legacy' (read: current)
// server.js implementations which aren't smart enough to read
// the manifest and instead want all of the resources in a
// directory together so they can just point gzippo at it. we
// should remove this and make the server work from the
// manifest.
static: 'static',
staticCacheable: 'static_cacheable'
manifest: manifest
});
return "program.json";
}