| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 | /*! * serve-static * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * Copyright(c) 2014-2016 Douglas Christopher Wilson * MIT Licensed */'use strict'/** * Module dependencies. * @private */var encodeUrl = require('encodeurl')var escapeHtml = require('escape-html')var parseUrl = require('parseurl')var resolve = require('path').resolvevar send = require('send')var url = require('url')/** * Module exports. * @public */module.exports = serveStaticmodule.exports.mime = send.mime/** * @param {string} root * @param {object} [options] * @return {function} * @public */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  var opts = Object.create(options || null)  // fall-though  var fallthrough = opts.fallthrough !== false  // default redirect  var redirect = opts.redirect !== false  // headers listener  var setHeaders = opts.setHeaders  if (setHeaders && typeof setHeaders !== 'function') {    throw new TypeError('option setHeaders must be function')  }  // setup options for send  opts.maxage = opts.maxage || opts.maxAge || 0  opts.root = resolve(root)  // construct directory listener  var onDirectory = redirect    ? createRedirectDirectoryListener()    : createNotFoundDirectoryListener()  return function serveStatic (req, res, next) {    if (req.method !== 'GET' && req.method !== 'HEAD') {      if (fallthrough) {        return next()      }      // method not allowed      res.statusCode = 405      res.setHeader('Allow', 'GET, HEAD')      res.setHeader('Content-Length', '0')      res.end()      return    }    var forwardError = !fallthrough    var originalUrl = parseUrl.original(req)    var path = parseUrl(req).pathname    // make sure redirect occurs at mount    if (path === '/' && originalUrl.pathname.substr(-1) !== '/') {      path = ''    }    // create send stream    var stream = send(req, path, opts)    // add directory handler    stream.on('directory', onDirectory)    // add headers listener    if (setHeaders) {      stream.on('headers', setHeaders)    }    // add file listener for fallthrough    if (fallthrough) {      stream.on('file', function onFile () {        // once file is determined, always forward error        forwardError = true      })    }    // forward errors    stream.on('error', function error (err) {      if (forwardError || !(err.statusCode < 500)) {        next(err)        return      }      next()    })    // pipe    stream.pipe(res)  }}/** * Collapse all leading slashes into a single slash * @private */function collapseLeadingSlashes (str) {  for (var i = 0; i < str.length; i++) {    if (str.charCodeAt(i) !== 0x2f /* / */) {      break    }  }  return i > 1    ? '/' + str.substr(i)    : str} /** * Create a minimal HTML document. * * @param {string} title * @param {string} body * @private */function createHtmlDocument (title, body) {  return '<!DOCTYPE html>\n' +    '<html lang="en">\n' +    '<head>\n' +    '<meta charset="utf-8">\n' +    '<title>' + title + '</title>\n' +    '</head>\n' +    '<body>\n' +    '<pre>' + body + '</pre>\n' +    '</body>\n' +    '</html>\n'}/** * Create a directory listener that just 404s. * @private */function createNotFoundDirectoryListener () {  return function notFound () {    this.error(404)  }}/** * Create a directory listener that performs a redirect. * @private */function createRedirectDirectoryListener () {  return function redirect (res) {    if (this.hasTrailingSlash()) {      this.error(404)      return    }    // get original URL    var originalUrl = parseUrl.original(this.req)    // append trailing slash    originalUrl.path = null    originalUrl.pathname = collapseLeadingSlashes(originalUrl.pathname + '/')    // reformat the URL    var loc = encodeUrl(url.format(originalUrl))    var doc = createHtmlDocument('Redirecting', 'Redirecting to <a href="' + escapeHtml(loc) + '">' +      escapeHtml(loc) + '</a>')    // send redirect response    res.statusCode = 301    res.setHeader('Content-Type', 'text/html; charset=UTF-8')    res.setHeader('Content-Length', Buffer.byteLength(doc))    res.setHeader('Content-Security-Policy', "default-src 'self'")    res.setHeader('X-Content-Type-Options', 'nosniff')    res.setHeader('Location', loc)    res.end(doc)  }}
 |