123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- var crypto = require('crypto')
- var fs = require('fs')
- var zlib = require('zlib')
- var path = require('path')
- var mime = require('mime-types')
- var compressible = require('compressible')
- var readDir = require('fs-readdir-recursive')
- var debug = require('debug')('koa-static-cache')
- module.exports = function staticCache(dir, options, files) {
- if (typeof dir === 'object') {
- files = options
- options = dir
- dir = null
- }
- options = options || {}
- options.prefix = (options.prefix || '').replace(/\/$/, '') + '/'
- files = files || options.files || Object.create(null)
- dir = dir || options.dir || process.cwd()
- var enableGzip = !!options.gzip
- readDir(dir).forEach(function (name) {
- name = name.replace(/\\/g, '/')
- var pathname = options.prefix + name
- var obj = files[pathname] = {}
- var filename = obj.path = path.join(dir, name)
- var stats = fs.statSync(filename)
- var buffer = fs.readFileSync(filename)
- obj.cacheControl = options.cacheControl
- obj.maxAge = options.maxAge || 0
- obj.type = obj.mime = mime.lookup(pathname) || 'application/octet-stream'
- obj.mtime = stats.mtime.toUTCString()
- obj.length = stats.size
- obj.md5 = crypto.createHash('md5').update(buffer).digest('base64')
- debug('file: ' + JSON.stringify(obj, null, 2))
- if (options.buffer)
- obj.buffer = buffer
- buffer = null
- })
- if (options.alias) {
- Object.keys(options.alias).forEach(function (key) {
- var value = options.alias[key]
- if (files[value]) {
- files[key] = files[value]
- debug('aliasing ' + value + ' as ' + key)
- }
- })
- }
- return function* staticCache(next) {
- var file = files[safeDecodeURIComponent(this.path)]
- if (!file)
- return yield* next
- switch (this.method) {
- case 'HEAD':
- case 'GET':
- this.status = 200
- if (enableGzip) this.vary('Accept-Encoding')
- if (!file.buffer) {
- var stats = yield stat(file.path)
- if (stats.mtime > new Date(file.mtime)) {
- file.mtime = stats.mtime.toUTCString()
- file.md5 = null
- file.length = stats.size
- }
- }
- this.response.lastModified = file.mtime
- if (file.md5) this.response.etag = file.md5
- if (this.fresh)
- return this.status = 304
- this.type = file.type
- this.length = file.zipBuffer ? file.zipBuffer.length : file.length
- this.set('Cache-Control', file.cacheControl || 'public, max-age=' + file.maxAge)
- if (file.md5) this.set('Content-MD5', file.md5)
- if (this.method === 'HEAD')
- return
- var acceptGzip = this.acceptsEncodings('gzip') === 'gzip';
- if (file.zipBuffer) {
- if (acceptGzip) {
- this.set('Content-Encoding', 'gzip')
- this.body = file.zipBuffer
- } else {
- this.body = file.buffer
- }
- return
- }
- var shouldGzip = enableGzip
- && file.length > 1024
- && acceptGzip
- && compressible(file.type)
- if (file.buffer) {
- if (shouldGzip) {
- file.zipBuffer = yield gzip(file.buffer)
- this.set('Content-Encoding', 'gzip')
- this.body = file.zipBuffer
- } else {
- this.body = file.buffer
- }
- return
- }
- var stream = fs.createReadStream(file.path)
- // update file hash
- if (!file.md5) {
- var hash = crypto.createHash('md5')
- stream.on('data', hash.update.bind(hash))
- stream.on('end', function () {
- file.md5 = hash.digest('base64')
- })
- }
- this.body = stream
- // enable gzip will remove content length
- if (shouldGzip) {
- this.remove('Content-Length')
- this.set('Content-Encoding', 'gzip')
- this.body = stream.pipe(zlib.createGzip())
- }
- return
- case 'OPTIONS':
- this.status = 204
- this.set('Allow', 'HEAD,GET,OPTIONS')
- return
- default:
- this.status = 405
- this.set('Allow', 'HEAD,GET,OPTIONS')
- return
- }
- }
- }
- var stat = function (file) {
- return function (done) {
- fs.stat(file, done)
- }
- }
- function gzip(buf) {
- return function (done) {
- zlib.gzip(buf, done)
- }
- }
- function safeDecodeURIComponent(text) {
- try {
- return decodeURIComponent(text);
- } catch (e) {
- return text;
- }
- }
|