Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

* First commit (just migrating from the diskcache module)

  • Loading branch information...
commit 91d99cad3d88b78193f425e5434c49bf41748d40 0 parents
@joehewitt authored
Showing with 227 additions and 0 deletions.
  1. +1 −0  .gitignore
  2. +13 −0 LICENSE
  3. +31 −0 README.md
  4. +163 −0 cache.js
  5. +19 −0 package.json
1  .gitignore
@@ -0,0 +1 @@
+node_modules
13 LICENSE
@@ -0,0 +1,13 @@
+Copyright 2011 Joe Hewitt
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
31 README.md
@@ -0,0 +1,31 @@
+express-cache
+===============
+
+Express middleware for caching responses on disk or in memory.
+
+Usage
+------------
+
+...
+
+Installation
+------------
+
+ $ npm install express-cache
+
+License
+-------
+
+Copyright 2011 Joe Hewitt
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
163 cache.js
@@ -0,0 +1,163 @@
+
+var dandy = require('dandy/errors');
+var buffers = require('buffers');
+var Cache = require('diskcache').Cache;
+
+var reContentType = /^(.*?);\s+charset=(.*?)$/i;
+
+// *************************************************************************************************
+
+module.exports = function(cache) {
+ if (!cache) {
+ cache = new Cache(null, false, true, true);
+ } else if (!(cache instanceof Cache)) {
+ cache = new Cache(cache.path, cache.useDisk, cache.useMem, cache.useGzip);
+ }
+
+ return function(req, res, next) {
+ try {
+ var cacheKey = req.url;//url.parse(req.url).pathname;
+ var body = buffers();
+
+ if (req.method.toUpperCase() == "GET") {
+ cacheLoad(cacheKey, function(err, entry, initial) {
+ if (err || !entry || !entry.body || !entry.body.length) {
+ render();
+ } else {
+ if (initial && entry.dependencies) {
+ monitor(entry.dependencies);
+ }
+
+ for (var name in entry.headers) {
+ res.header(name, entry.headers[name]);
+ }
+
+ sendFromCache(entry);
+ }
+ });
+ } else {
+ render();
+ }
+ } catch (exc) {
+ sendError(exc);
+ }
+
+ function render() {
+ var write = res.write;
+ res.write = function(data, encoding) {
+ body.push(new Buffer(data, encoding));
+ }
+
+ var end = res.end;
+ res.end = function(data, encoding) {
+ body.push(new Buffer(data, encoding));
+
+ res.write = write;
+ res.end = end;
+
+ // XXXjoe It's unfortunate we have to make a copy here, but passing the Buffers
+ // object directly through the system doesn't work... investigate!
+ body = body.slice();
+
+ sendFromApp(cacheKey, body);
+ }
+
+ // XXXjoe Make sure to unlock the cache if there is an error
+ cache.lock(cacheKey);
+ next();
+ }
+
+ function cacheLoad(cacheKey, cb) {
+ if (cache) {
+ cache.load(cacheKey, cb);
+ } else {
+ cb(0, null);
+ }
+ }
+
+ function cacheStore(cacheKey, entry, cb) {
+ if (cache) {
+ cache.store(cacheKey, entry, cb);
+ } else {
+ cb(0, entry);
+ }
+ }
+
+ function monitor(dependencies) {
+ if (cache && dependencies) {
+ cache.monitor(cacheKey, dependencies);
+ }
+ }
+
+ function sendFromApp(cacheKey, body) {
+ if (res.statusCode == 200 && !res.doNotCache) {
+ var headers = {};
+ module.exports.headersToCache.forEach(function(name) {
+ headers[name] = res.header(name);
+ });
+
+ var entry = {key: cacheKey, headers: headers, body: body};
+ if (res.dependencies) {
+ entry.dependencies = res.dependencies;
+ monitor(res.dependencies);
+ }
+
+ var contentType = res.header('content-type');
+ var m = reContentType.exec(contentType);
+ entry.mimeType = m ? m[1] : contentType;
+ entry.charset = m ? m[2] : null;
+
+ cacheStore(cacheKey, entry, function(err, entry) {
+ sendFromCache(entry);
+ });
+ } else {
+ cache.unlock(cacheKey);
+ res.send(body);
+ }
+ }
+
+ function sendFromCache(entry) {
+ var etag = res.header('etag');
+ var ifNoneMatch = req.header('if-none-match');
+ if (ifNoneMatch && ifNoneMatch == etag) {
+ res.send('', 304);
+ } else {
+ var body = entry.body;
+ if (entry.bodyZipped && canZipType(entry.mimeType) && requestAccepts(req, 'gzip')) {
+ body = entry.bodyZipped;
+ res.header('Content-Encoding', 'gzip');
+ }
+
+ res.header('Content-Length', body.length);
+ res.send(body, 200);
+ }
+ }
+
+ function sendError(err) {
+ dandy.logException(err);
+ res.send('Error: ' + err, {'Content-Type': 'text/html'}, 500);
+ }
+ }
+}
+
+function canZipType(mimeType) {
+ return mimeType.indexOf('image/') != 0
+ && module.exports.gzipBlacklist.indexOf(mimeType) == -1;
+}
+
+function requestAccepts(req, encoding) {
+ var accepts = 'accept-encoding' in req.headers ? req.header('accept-encoding').split(/\s*,\s*/) : [];
+ return accepts.indexOf(encoding) != -1;
+}
+
+// *************************************************************************************************
+
+// Whitelist of headers to cache - should it be a blacklist instead?
+module.exports.headersToCache = [
+ "content-type",
+ "etag",
+ "cache-control"
+];
+
+// Blacklist of mime types not to gzip
+module.exports.gzipBlacklist = [];
19 package.json
@@ -0,0 +1,19 @@
+{
+ "name": "express-cache",
+ "description": "Express middleware for caching responses on disk or in memory.",
+ "version": "0.0.1",
+ "homepage": "http://github.com/joehewitt/express-cache",
+ "repository": {
+ "type": "git",
+ "url" : "http://github.com/joehewitt/express-cache.git"
+ },
+ "keywords": ["express"],
+ "author": "Joe Hewitt <joe@joehewitt.com>",
+ "dependencies": {
+ "buffers": "",
+ "dandy": "",
+ "diskcache": ""
+ },
+ "engines": { "node": ">=0.4.0" },
+ "main": "./cache"
+}
Please sign in to comment.
Something went wrong with that request. Please try again.