// @see https://devcenter.heroku.com/articles/nodejs#write-your-app

var express = require('express')
  , fs      = require('fs') // node
  , path    = require('path') //node
  , gm      = require('gm')
  , http    = require('http') // node
  , https   = require('https') // node
  , mime    = require('mime') // express
  , url     = require('url') // node
  , md5     = require('MD5')
  , glob    = require('glob')
  // @see http://aaronheckmann.posterous.com/graphicsmagick-on-heroku-with-nodejs
  , imageMagick = gm.subClass({imageMagick: true})
  , app = express.createServer(express.logger())
  , mimeTypes = [
    'image/jpeg',
    'image/png',
    'image/gif',
    'image/jpg'
  ];

app.get('/crossdomain.xml', function(req, res, next){
    res.setHeader("Access-Control-Allow-Origin", "*");
    res.setHeader("Access-Control-Allow-Headers", "X-Requested-With");
    res.setHeader("Content-Type", "text/xml");
    var filePath = path.join(__dirname, "crossdomain.xml");
    var stream = fs.createReadStream(filePath);
    stream.pipe(res);
});

// Set default file extension for image/jpg
mime.define({
  'image/jpeg': ['jpg'],
  'image/jpg': ['jpg']
});

app.get('/:url/:width/:height', function (req, res, next) {

    res.setHeader("Access-Control-Allow-Origin", "*");
    res.setHeader("Access-Control-Allow-Headers", "X-Requested-With");
  var width = req.params.width
    , height = req.params.height
    , retrieve = function (remote) {
      // @see http://nodejs.org/api/url.html#url_url
      var parts = url.parse(remote);
      if (['https:', 'http:'].indexOf(parts.protocol) === -1) {
        return res.send('Expected URI scheme to be HTTP or HTTPS', 404);
      }
      if (!parts.hostname) {
        return res.send('Expected URI host to be non-empty', 404);
      }

      // see if its cached
      var hash = width+'x'+height+'-'+md5(remote);
      var cachedPath = '/tmp/'+hash;

      try {
        //var fd = fs.openSync(cachedPath, 'r');
        var files = glob.sync(cachedPath+'.*');
        //console.log(files);
        if(files.length){

          var fr = fs.readFileSync(files[0]);
          if(fr) {
            console.log('Cache hit: '+files[0]);
            //res.writeHead(304, {
              //'Content-Type': mime.lookup(files[0]),
              //'Cache-Control': 'max-age=31536000, public', // 1 year
            //});
            res.setHeader('Content-Type', mime.lookup(files[0]));
            //var img = new Buffer(fr, 'base64');
            return res.status(200).send(fr);
          }
          console.log('File not read');
        }else {
          console.log('Cache miss: '+cachedPath);
        }
      } catch (e) {
        console.log(e);
        //console.log('Cached file '+cachedPath+' not found');
      }

      var agent = parts.protocol === 'http:' ? http : https
        // @see http://nodejs.org/api/http.html#http_http_get_options_callback
        , request = agent.get(remote, function (res2) {
          // @see http://nodejs.org/api/http.html#http_response_statuscode
          if ([301, 302].indexOf(res2.statusCode) !== -1 && res2.headers['location']) {
            return retrieve(res2.headers['location']);
          }

          // The remote image must return status code 200.
          if (res2.statusCode !== 200) {
            return res.status(404).send('Expected response code 200, got ' + res2.statusCode, 404);
          }

          // The remote image must be a valid content type.
          // @see http://nodejs.org/api/http.html#http_request_headers
          var mimeType = res2.headers['content-type'].replace(/;.+/, '');
          if (mimeTypes.indexOf(mimeType) === -1) {
            if (mimeType == 'video/mp4') {
              var writeStream = fs.createWriteStream(cachedPath + '.' + mime.extension(mimeType));
              res2.pipe(writeStream);
              return res2.pipe(res);
            }

            return res.send('Expected content type ' + mimeTypes.join(', ') + ', got ' + mimeType, 404);
          }

          // @see https://github.com/aheckmann/gm#constructor
          imageMagick(res2, 'image.' + mime.extension(mimeType))
          // @see http://www.imagemagick.org/Usage/thumbnails/#cut
          .resize(width, height + '^>')
          .gravity('Center') // faces are most often near the center
          .extent(width, height)
          .stream(function (err, stdout, stderr) {
            if (err) return next(err);
            stdout.setMaxListeners(500);
            // Log errors in production.
            stderr.pipe(process.stderr);
            // @see http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Expiration.html
//            res.writeHead(200, {
//              'Content-Type': mimeType,
//              'Cache-Control': 'max-age=31536000, public', // 1 year
//            });
            console.log('Cache save: '+cachedPath+'.'+mime.extension(mimeType));
            var writeStream = fs.createWriteStream(cachedPath+'.'+mime.extension(mimeType));

            stdout.pipe(writeStream);
            stdout.pipe(res);
          });
        }).on('error', next);

      // Timeout after five seconds. Better luck next time.
      request.setTimeout(5000, function () {
        return res.send(504);
      });
    };

  // Validate query string parameters.
  if (whitelist.length) {
    var parts = url.parse(req.params.url)
      , any = false;
    for (var _i = 0, _len = whitelist.length; _i < _len; _i++) {
      if (whitelist[_i].test(parts.hostname)) {
        any = true;
        break;
      }
    }
    if (!any) { // if none
      return res.send('Expected URI host to be whitelisted', 404);
    }
  }
  if (isNaN(parseInt(width))) {
    return res.send('Expected width to be an integer', 404);
  }
  if (parseInt(width) > 1000) {
    return res.send('Expected width to be less than or equal to 1000', 404);
  }
  if (isNaN(parseInt(height))) {
    return res.send('Expected height to be an integer', 404);
  }
  if (parseInt(height) > 1000) {
    return res.send('Expected height to be less than or equal to 1000', 404);
  }

  retrieve(req.params.url);
});

var port = process.env.PORT || 5000;
var whitelist = process.env.WHITELIST || []; // [/\.gov$/, /google\.com$/]
app.listen(port, function () {
  console.log('Listening on ' + port);
});

@@ -1344,16 +1344,76 @@
 With%22);%0A
+    res.setHeader(%22Cache-Control%22, %22public, max-age: 300%22);%0A
   var wi
@@ -2233,32 +2233,77 @@
 t: '+files%5B0%5D);%0A
+            res.setHeader('X-Cache', 'HIT');%0A
             //re

Add a cache-control header