arnaucube
arnaucube/thoughtsPublic Self-hosted repository
thoughts/node_modules/express/node_modules/serve-static/index.js
node_modules/express/node_modules/serve-static/index.jsRaw
  1/*!
  2 * serve-static
  3 * Copyright(c) 2010 Sencha Inc.
  4 * Copyright(c) 2011 TJ Holowaychuk
  5 * Copyright(c) 2014-2016 Douglas Christopher Wilson
  6 * MIT Licensed
  7 */
  8
  9'use strict'
 10
 11/**
 12 * Module dependencies.
 13 * @private
 14 */
 15
 16var encodeUrl = require('encodeurl')
 17var escapeHtml = require('escape-html')
 18var parseUrl = require('parseurl')
 19var resolve = require('path').resolve
 20var send = require('send')
 21var url = require('url')
 22
 23/**
 24 * Module exports.
 25 * @public
 26 */
 27
 28module.exports = serveStatic
 29module.exports.mime = send.mime
 30
 31/**
 32 * @param {string} root
 33 * @param {object} [options]
 34 * @return {function}
 35 * @public
 36 */
 37
 38function serveStatic (root, options) {
 39  if (!root) {
 40    throw new TypeError('root path required')
 41  }
 42
 43  if (typeof root !== 'string') {
 44    throw new TypeError('root path must be a string')
 45  }
 46
 47  // copy options object
 48  var opts = Object.create(options || null)
 49
 50  // fall-though
 51  var fallthrough = opts.fallthrough !== false
 52
 53  // default redirect
 54  var redirect = opts.redirect !== false
 55
 56  // headers listener
 57  var setHeaders = opts.setHeaders
 58
 59  if (setHeaders && typeof setHeaders !== 'function') {
 60    throw new TypeError('option setHeaders must be function')
 61  }
 62
 63  // setup options for send
 64  opts.maxage = opts.maxage || opts.maxAge || 0
 65  opts.root = resolve(root)
 66
 67  // construct directory listener
 68  var onDirectory = redirect
 69    ? createRedirectDirectoryListener()
 70    : createNotFoundDirectoryListener()
 71
 72  return function serveStatic (req, res, next) {
 73    if (req.method !== 'GET' && req.method !== 'HEAD') {
 74      if (fallthrough) {
 75        return next()
 76      }
 77
 78      // method not allowed
 79      res.statusCode = 405
 80      res.setHeader('Allow', 'GET, HEAD')
 81      res.setHeader('Content-Length', '0')
 82      res.end()
 83      return
 84    }
 85
 86    var forwardError = !fallthrough
 87    var originalUrl = parseUrl.original(req)
 88    var path = parseUrl(req).pathname
 89
 90    // make sure redirect occurs at mount
 91    if (path === '/' && originalUrl.pathname.substr(-1) !== '/') {
 92      path = ''
 93    }
 94
 95    // create send stream
 96    var stream = send(req, path, opts)
 97
 98    // add directory handler
 99    stream.on('directory', onDirectory)
100
101    // add headers listener
102    if (setHeaders) {
103      stream.on('headers', setHeaders)
104    }
105
106    // add file listener for fallthrough
107    if (fallthrough) {
108      stream.on('file', function onFile () {
109        // once file is determined, always forward error
110        forwardError = true
111      })
112    }
113
114    // forward errors
115    stream.on('error', function error (err) {
116      if (forwardError || !(err.statusCode < 500)) {
117        next(err)
118        return
119      }
120
121      next()
122    })
123
124    // pipe
125    stream.pipe(res)
126  }
127}
128
129/**
130 * Collapse all leading slashes into a single slash
131 * @private
132 */
133function collapseLeadingSlashes (str) {
134  for (var i = 0; i < str.length; i++) {
135    if (str[i] !== '/') {
136      break
137    }
138  }
139
140  return i > 1
141    ? '/' + str.substr(i)
142    : str
143}
144
145/**
146 * Create a directory listener that just 404s.
147 * @private
148 */
149
150function createNotFoundDirectoryListener () {
151  return function notFound () {
152    this.error(404)
153  }
154}
155
156/**
157 * Create a directory listener that performs a redirect.
158 * @private
159 */
160
161function createRedirectDirectoryListener () {
162  return function redirect () {
163    if (this.hasTrailingSlash()) {
164      this.error(404)
165      return
166    }
167
168    // get original URL
169    var originalUrl = parseUrl.original(this.req)
170
171    // append trailing slash
172    originalUrl.path = null
173    originalUrl.pathname = collapseLeadingSlashes(originalUrl.pathname + '/')
174
175    // reformat the URL
176    var loc = encodeUrl(url.format(originalUrl))
177    var msg = 'Redirecting to <a href="' + escapeHtml(loc) + '">' + escapeHtml(loc) + '</a>\n'
178    var res = this.res
179
180    // send redirect response
181    res.statusCode = 301
182    res.setHeader('Content-Type', 'text/html; charset=UTF-8')
183    res.setHeader('Content-Length', Buffer.byteLength(msg))
184    res.setHeader('X-Content-Type-Options', 'nosniff')
185    res.setHeader('Location', loc)
186    res.end(msg)
187  }
188}