diff --git a/lib/response/file.js b/lib/response/file.js index 13d0f791f..b2e34cf3b 100755 --- a/lib/response/file.js +++ b/lib/response/file.js @@ -7,12 +7,14 @@ var Mime = require('mime'); var Err = require('../error'); var Utils = require('../utils'); var Stream = require('./stream'); +var LRU = require('lru-cache'); +var Crypto = require('crypto'); // Declare internals var internals = {}; - +internals.fileEtags = LRU(); // File response (Base -> Generic -> Stream -> File) @@ -50,8 +52,23 @@ internals.File.prototype._prepare = function (request, callback) { Stream.call(self, stream); self._headers['Content-Type'] = Mime.lookup(self._filePath) || 'application/octet-stream'; self._headers['Content-Length'] = stat.size; - self._headers['Last-Modified'] = new Date(stat.mtime).toUTCString(); - self._headers.etag = JSON.stringify([stat.ino, stat.size, Date.parse(stat.mtime)].join('-')); + + // Use stat info for an LRU cache key. + var cachekey = [self._filePath, stat.ino, stat.size, Date.parse(stat.mtime)].join('-'); + + // The etag must hash the file contents in order to be consistent between nodes. + if (internals.fileEtags.has(cachekey)) { + self._headers.etag = JSON.stringify(internals.fileEtags.get(cachekey)); + } else { + var hash = Crypto.createHash('md5'); + stream.on('data', function (chunk) { + hash.update(chunk); + }) + stream.on('end', function () { + internals.fileEtags.set(cachekey, hash.digest("hex")); + }) + } + self._headers['Content-Disposition'] = 'inline; filename=' + encodeURIComponent(fileName); return Stream.prototype._prepare.call(self, request, callback); diff --git a/package.json b/package.json index 38f9caf3e..fcd76c658 100755 --- a/package.json +++ b/package.json @@ -36,7 +36,8 @@ "catbox": "0.1.x", "cryptiles": "0.0.x", "iron": "0.1.x", - "semver": "1.1.0" + "semver": "1.1.0", + "lru-cache": "~2.2.2" }, "devDependencies": { "mocha": "1.x.x", diff --git a/test/integration/response.js b/test/integration/response.js index df3a9dca1..cb38eee4d 100755 --- a/test/integration/response.js +++ b/test/integration/response.js @@ -544,23 +544,6 @@ describe('Response', function () { }); }); - it('returns a 304 when the request has a future modifed-since', function (done) { - - server.start(function () { - - var date = new Date(Date.now()); - var headers = { - 'if-modified-since': new Date(date.setFullYear(date.getFullYear() + 1)).toUTCString() - }; - - Request.get({ url: 'http://localhost:17082/file', headers: headers }, function (err, res) { - - expect(res.statusCode).to.equal(304); - done(); - }); - }); - }); - it('returns a gzipped file in the response when the request accepts gzip', function (done) { server.start(function () {