lib/utils/serve-static.js
/*!
* serve-static
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* Copyright(c) 2014 Douglas Christopher Wilson
* MIT Licensed
*
* This is really just for development. In production static files should be handled by static file server or cache.
*
* Added prefix path ability. If expressjs/serve-static will support this in the future this can be swapped out.
*/
/**
* Module dependencies.
*/
'use strict';
var escapeHtml, merge, parseurl, resolve, send, url, setHeaders, originalUrl, path, hasTrailingSlash, stream;
escapeHtml = require('escape-html');
merge = require('utils-merge');
parseurl = require('parseurl');
resolve = require('path').resolve;
send = require('send');
url = require('url');
/**
* @param {String} root
* @param {Object} options
* @return {Function}
* @api public
*/
exports = module.exports = function serveStatic (prefix,root, options) {
if (!root) {
throw new TypeError('root path required')
}
if (typeof root !== 'string') {
throw new TypeError('root path must be a string')
}
// copy options object
options = merge({}, options)
// resolve root to absolute
root = resolve(root)
// default redirect
var redirect = options.redirect !== false
// headers listener
setHeaders = options.setHeaders;
delete options.setHeaders
if (setHeaders && typeof setHeaders !== 'function') {
throw new TypeError('option setHeaders must be function')
}
// setup options for send
options.maxage = options.maxage || options.maxAge || 0
options.root = root
return function serveStatic(req, res, next) {
if (req.method !== 'GET' && req.method !== 'HEAD') {
return next()
}
var opts = merge({}, options)
originalUrl = parseurl.original(req)
path = parseurl(req).pathname
if (prefix) {
if (path.substring(0,prefix.length) !== prefix) {
return next();
}
path = path.substring(prefix.length);
}
hasTrailingSlash = originalUrl.pathname[originalUrl.pathname.length - 1] === '/'
if (path === '/' && !hasTrailingSlash) {
// make sure redirect occurs at mount
path = ''
}
// create send stream
stream = send(req, path, opts)
if (redirect) {
// redirect relative to originalUrl
stream.on('directory', function redirect() {
if (hasTrailingSlash) {
return next()
}
originalUrl.pathname += '/'
var target = url.format(originalUrl)
res.statusCode = 303
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.setHeader('Location', target)
//TODO translation string support
res.end('Redirecting to <a href="' + escapeHtml(target) + '">' + escapeHtml(target) + '</a>\n')
})
} else {
// forward to next middleware on directory
stream.on('directory', next)
}
// add headers listener
if (setHeaders) {
stream.on('headers', setHeaders)
}
// forward non-404 errors
stream.on('error', function error(err) {
next(err.status === 404 ? null : err)
})
// pipe
stream.pipe(res)
}
}
/**
* Expose mime module.
*
* If you wish to extend the mime table use this
* reference to the "mime" module in the npm registry.
*/
exports.mime = send.mime