File: index.js

package info (click to toggle)
groovebasin 1.4.0-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd, stretch
  • size: 1,400 kB
  • ctags: 1,281
  • sloc: makefile: 6; sh: 3
file content (110 lines) | stat: -rw-r--r-- 3,284 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
var zlib = require('zlib');
var fs = require('fs');
var stream = require('stream');
var util = require('util');
var path = require('path');
var Pend = require('pend');
var findit = require('findit2');
var mime = require('mime');
var url = require('url');
var StreamSink = require('streamsink');
var crypto = require('crypto');

module.exports = createGzipStaticMiddleware;

function createGzipStaticMiddleware(options, cb) {
  options = options || {};
  var dir = options.dir || "public";
  var ignoreFile = options.ignoreFile || defaultIgnoreFile;
  var aliases = options.aliases || [['/', '/index.html']];
  var followSymlinks = (options.followSymlinks == null) ? true : !!options.followSymlinks;

  var cache = {};
  var pend = new Pend();
  var walker = findit(dir, {followSymlinks: followSymlinks});
  walker.on('error', function(err) {
    walker.stop();
    cb(err);
  });
  walker.on('file', function(file, stat, linkPath) {
    var usePath = linkPath || file;
    if (ignoreFile(usePath)) return;
    var relName = '/' + path.relative(dir, usePath);
    var sink = new StreamSink();
    var inStream = fs.createReadStream(file);
    var cacheObj;
    cache[relName] = cacheObj = {
      sink: sink,
      mime: mime.lookup(relName),
      mtime: stat.mtime,
      hash: null,
    };
    var gzipPendCb, hashPendCb;
    inStream.on('error', function(err) {
      if (err.code === 'EISDIR') {
        delete cache[relName];
        gzipPendCb();
        hashPendCb();
      } else {
        walker.stop();
        gzipPendCb(err);
        hashPendCb(err);
      }
    });
    pend.go(function(cb) {
      gzipPendCb = cb;
      inStream.pipe(zlib.createGzip()).pipe(sink);
      sink.on('finish', cb);
    });
    pend.go(function(cb) {
      hashPendCb = cb;
      var hashSink = new StreamSink();
      inStream.pipe(crypto.createHash('sha1')).pipe(hashSink);
      hashSink.on('finish', function() {
        cacheObj.hash = hashSink.toString('base64');
        cb();
      });
    });
  });
  walker.on('end', function() {
    pend.wait(function(err) {
      if (err) return cb(err);
      aliases.forEach(function(alias) {
        cache[alias[0]] = cache[alias[1]];
      });
      cb(null, middleware);
    });
    function middleware(req, resp, next) {
      var parsedUrl = url.parse(req.url);
      var c = cache[parsedUrl.pathname];
      if (!c) return next();
      if (req.headers['if-none-match'] === c.hash) {
        resp.statusCode = 304;
        resp.end();
        return;
      }
      var ifModifiedSince = new Date(req.headers['if-modified-since']);
      if (!isNaN(ifModifiedSince) && c.mtime <= ifModifiedSince) {
        resp.statusCode = 304;
        resp.end();
        return;
      }

      var sink = c.sink;
      resp.setHeader('Content-Type', c.mime);
      resp.setHeader('Cache-Control', 'max-age=0, must-revalidate');
      resp.setHeader('ETag', c.hash);
      if (req.headers['accept-encoding'] == null) {
        sink.createReadStream().pipe(zlib.createGunzip()).pipe(resp);
      } else {
        resp.setHeader('Content-Encoding', 'gzip');
        sink.createReadStream().pipe(resp);
      }
    }
  });
}

function defaultIgnoreFile(file) {
  var basename = path.basename(file);
  return /^\./.test(basename) || /~$/.test(basename);
}