|
|
/*! * serve-static * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * Copyright(c) 2014 Douglas Christopher Wilson * MIT Licensed */
/** * Module dependencies. */
var escapeHtml = require('escape-html'); var merge = require('utils-merge'); var parseurl = require('parseurl'); var resolve = require('path').resolve; var send = require('send'); var url = require('url');
/** * @param {String} root * @param {Object} options * @return {Function} * @api public */
exports = module.exports = function serveStatic(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
var 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) var originalUrl = parseurl.original(req) var path = parseurl(req).pathname var hasTrailingSlash = originalUrl.pathname[originalUrl.pathname.length - 1] === '/'
if (path === '/' && !hasTrailingSlash) { // make sure redirect occurs at mount
path = '' }
// create send stream
var stream = send(req, path, opts)
if (redirect) { // redirect relative to originalUrl
stream.on('directory', function redirect() { if (hasTrailingSlash) { return next() }
// append trailing slash
originalUrl.pathname = collapseLeadingSlashes(originalUrl.pathname + '/')
// reformat the URL
var target = url.format(originalUrl)
// send redirect response
res.statusCode = 303 res.setHeader('Content-Type', 'text/html; charset=utf-8') res.setHeader('Location', target) 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
/** * Collapse all leading slashes into a single slash * @private */ function collapseLeadingSlashes(str) { for (var i = 0; i < str.length; i++) { if (str[i] !== '/') { break } }
return i > 1 ? '/' + str.substr(i) : str }
|