Merge pull request #36 from infindex/master

Fix to handle weird scenarios where the request urls to assets contain query string
This commit is contained in:
Austin King 2013-09-24 08:35:56 -07:00
Родитель d65733f965 ee1bf95802
Коммит 7cf3731edb
2 изменённых файлов: 119 добавлений и 12 удалений

Просмотреть файл

@ -9,7 +9,8 @@ var crypto = require('crypto'),
format = require('./compat').format,
fs = require('fs'),
path = require('path'),
und = require('underscore');
und = require('underscore'),
url = require('url');
var existsSync = fs.existsSync || path.existsSync,
opts,
@ -48,8 +49,10 @@ exports.setup = function (assets, options) {
// * the filename is a key in our opts[family]
// * or the file exists on the filesystem
var prefix = escape_regex(no_url(opts.prefix)),
req_url = url.parse(req.url),
uri = req_url.pathname ? req_url.pathname : '',
fmt = format('^\\/%s[a-f0-9]{10}', prefix),
m = req.url.match(new RegExp(fmt)),
m = uri.match(new RegExp(fmt)),
true_path, exists;
if (typeof resp.local === 'function') {
@ -68,15 +71,15 @@ exports.setup = function (assets, options) {
}
if (m && m.index === 0) {
// extract the hash
var hash = req.url.slice(prefix.length + 1, prefix.length + 11);
var hash = uri.slice(prefix.length + 1, prefix.length + 11);
// 10 first characters of md5 + 1 for slash
var url = req.url.slice(prefix.length + 11);
var true_path = opts.url_to_paths[url] || path.join(opts.root, url);
var _url = uri.slice(prefix.length + 11);
true_path = opts.url_to_paths[_url] || path.join(opts.root, _url);
exists = false;
if (opts.prefix !== '' &&
req.url.indexOf('/' + opts.prefix) === 0) {
uri.indexOf('/' + opts.prefix) === 0) {
exists = true;
} else if (assets[true_path]) {
exists = true;
@ -114,7 +117,8 @@ exports.setup = function (assets, options) {
if (exists === true && hash === actualHash) {
resp.setHeader('Cache-Control', 'public, max-age=31536000');
req.url = url;
// append back the query string and Hash if any
req.url = _url + (req_url.query ? '?' + req_url.query : '') + (req_url.hash ? req_url.hash : '');
if (opts.control_headers === true) {
resp.on('header', function () {
// Relax other middleware... I got this one
@ -194,18 +198,22 @@ var hashify = function (resource, hash) {
};
var prod_or_dev_tags = function (filename, link_fmt, hash) {
var req_url = url.parse(filename),
uri = req_url.pathname ? req_url.pathname : '';
if (opts.production !== true &&
_assets[filename]) {
return und.map(_assets[filename], function (f) {
return format(link_fmt, hashify(f, hash));
}).join('\n');
_assets[uri]) {
return und.map(_assets[uri], function (f) {
var asset_url = url.parse(f),
asset_uri = asset_url.pathname ? asset_url.pathname : '';
return format(link_fmt, hashify(asset_uri + (req_url.query ? '?' + req_url.query : '') + (asset_url.hash ? asset_url.hash : ''), hash));
}).join('\n');
}
return format(link_fmt, hashify(filename, hash));
};
var cachify_js = exports.cachify_js = function (filename, options) {
if (! options) options = {};
var link_fmt = '<script src="%s"'
var link_fmt = '<script src="%s"';
/**
* indicate to a browser that the script is meant to be executed after the

Просмотреть файл

@ -165,6 +165,34 @@ exports.setup = nodeunit.testCase({
test.done();
});
},
"Development mode with url query string": function (test) {
var assets = make_assets(),
mddlwr;
mddlwr = cachify.setup(assets, {
root: '/tmp',
production: false
});
var links = cachify.cachify_js("/js/main.min.js?triedPassive=true").split('\n');
test.equal(links.length, assets["/js/main.min.js"].length,
"All script tags created");
test.equal(links[0], '<script src="/js/lib/jquery.js?triedPassive=true"></script>',
"No hashes in all urls during development");
test.equal(links[3], '<script src="/js/font-loader.js?triedPassive=true#with_fragment_id"></script>',
"Fragment identifier in URL is preserved");
test.equal(cachify.uncached_resources.indexOf("/js/font-loader.js?triedPassive=true#with_fragment_id"), -1,
"Fragment identifiers are never sent to server, search on disk for resource by removing fragment id");
var files = cachify.cachify("/js/main.min.js?triedPassive=true").split('\n');
test.equal(files.length, assets["/js/main.min.js"].length,
"All urls created");
test.equal(files[0], '/js/lib/jquery.js?triedPassive=true',
"No hashes in all urls during development");
var hints = cachify.cachify_prefetch("/js/main.min.js?triedPassive=true").split('\n');
test.ok(hints.length, "Multiple link tags");
test.equal(hints[0], '<link rel="prefetch" href="/js/lib/jquery.js?triedPassive=true">');
test.done();
},
"Production mode": function (test) {
var assets = make_assets(),
req = {
@ -206,6 +234,28 @@ exports.setup = nodeunit.testCase({
test.done();
});
},
"Production mode with url query string" : function (test) {
var assets = make_assets(),
req = {
url: '/d41d8cd98f/js/main.min.js?triedPassive=true'
},
resp = get_resp(),
mddlwr;
mddlwr = cachify.setup(
assets, {
root: '/tmp'
});
mddlwr(req, resp, function () {
test.ok(resp.state.cachify_prefetch);
test.ok(resp.state.cachify_js);
test.ok(resp.state.cachify_css);
test.ok(resp.state.cachify);
test.ok(resp.state.header > 0);
test.equal(req.url, '/js/main.min.js?triedPassive=true');
test.done();
});
},
"Production mode with absolute URL in prefix": function (test) {
var assets = make_assets(),
req = {
@ -235,6 +285,29 @@ exports.setup = nodeunit.testCase({
test.done();
});
},
"Production mode with absolute URL in prefix and url query string" : function (test) {
var assets = make_assets(),
req = {
url: '/v/d41d8cd98f/js/main.min.js?triedPassive=true'
},
resp = get_resp(),
mddlwr;
mddlwr = cachify.setup(
assets, {
root: '/tmp',
prefix: 'http://example.com/v/'
});
mddlwr(req, resp, function () {
test.ok(resp.state.cachify_prefetch);
test.ok(resp.state.cachify_js);
test.ok(resp.state.cachify_css);
test.ok(resp.state.cachify);
test.ok(resp.state.header > 0);
test.equal(req.url, '/js/main.min.js?triedPassive=true');
test.done();
});
},
"Custom prefix works with non-file assets": function (test) {
var assets = make_assets(),
req = {
@ -261,6 +334,32 @@ exports.setup = nodeunit.testCase({
test.done();
});
},
"Custom prefix with non-file assets and url query string": function (test) {
var assets = make_assets(),
req = {
url: '/cachify/d41d8cd98f/other?triedPassive=true'
},
resp = get_resp(),
mddlwr;
mddlwr = cachify.setup(
assets, {
prefix: 'cachify',
root: '/tmp'
});
var link = cachify.cachify_js("/other", {hash: 'd41d8cd98f'});
test.equal(link, '<script src="/cachify/d41d8cd98f/other"></script>');
mddlwr(req, resp, function () {
test.ok(resp.state.cachify_prefetch);
test.ok(resp.state.cachify_js);
test.ok(resp.state.cachify_css);
test.ok(resp.state.cachify);
test.ok(resp.state.header > 0);
test.equal(req.url, '/other?triedPassive=true');
test.done();
});
},
"Production mode, mismatched checksum, not substituted": function (test) {
var assets = make_assets(),
req = {