AK-21/Graphite-Industrial-Intelligence
0
1/*!2 * serve-static3 * Copyright(c) 2010 Sencha Inc.4 * Copyright(c) 2011 TJ Holowaychuk5 * Copyright(c) 2014-2016 Douglas Christopher Wilson6 * MIT Licensed7 */8 9'use strict'10 11/**12 * Module dependencies.13 * @private14 */15 16var encodeUrl = require('encodeurl')17var escapeHtml = require('escape-html')18var parseUrl = require('parseurl')19var resolve = require('path').resolve20var send = require('send')21var url = require('url')22 23/**24 * Module exports.25 * @public26 */27 28module.exports = serveStatic29 30/**31 * @param {string} root32 * @param {object} [options]33 * @return {function}34 * @public35 */36 37function serveStatic (root, options) {38 if (!root) {39 throw new TypeError('root path required')40 }41 42 if (typeof root !== 'string') {43 throw new TypeError('root path must be a string')44 }45 46 // copy options object47 var opts = Object.create(options || null)48 49 // fall-though50 var fallthrough = opts.fallthrough !== false51 52 // default redirect53 var redirect = opts.redirect !== false54 55 // headers listener56 var setHeaders = opts.setHeaders57 58 if (setHeaders && typeof setHeaders !== 'function') {59 throw new TypeError('option setHeaders must be function')60 }61 62 // setup options for send63 opts.maxage = opts.maxage || opts.maxAge || 064 opts.root = resolve(root)65 66 // construct directory listener67 var onDirectory = redirect68 ? createRedirectDirectoryListener()69 : createNotFoundDirectoryListener()70 71 return function serveStatic (req, res, next) {72 if (req.method !== 'GET' && req.method !== 'HEAD') {73 if (fallthrough) {74 return next()75 }76 77 // method not allowed78 res.statusCode = 40579 res.setHeader('Allow', 'GET, HEAD')80 res.setHeader('Content-Length', '0')81 res.end()82 return83 }84 85 var forwardError = !fallthrough86 var originalUrl = parseUrl.original(req)87 var path = parseUrl(req).pathname88 89 // make sure redirect occurs at mount90 if (path === '/' && originalUrl.pathname.substr(-1) !== '/') {91 path = ''92 }93 94 // create send stream95 var stream = send(req, path, opts)96 97 // add directory handler98 stream.on('directory', onDirectory)99 100 // add headers listener101 if (setHeaders) {102 stream.on('headers', setHeaders)103 }104 105 // add file listener for fallthrough106 if (fallthrough) {107 stream.on('file', function onFile () {108 // once file is determined, always forward error109 forwardError = true110 })111 }112 113 // forward errors114 stream.on('error', function error (err) {115 if (forwardError || !(err.statusCode < 500)) {116 next(err)117 return118 }119 120 next()121 })122 123 // pipe124 stream.pipe(res)125 }126}127 128/**129 * Collapse all leading slashes into a single slash130 * @private131 */132function collapseLeadingSlashes (str) {133 for (var i = 0; i < str.length; i++) {134 if (str.charCodeAt(i) !== 0x2f /* / */) {135 break136 }137 }138 139 return i > 1140 ? '/' + str.substr(i)141 : str142}143 144/**145 * Create a minimal HTML document.146 *147 * @param {string} title148 * @param {string} body149 * @private150 */151 152function createHtmlDocument (title, body) {153 return '<!DOCTYPE html>\n' +154 '<html lang="en">\n' +155 '<head>\n' +156 '<meta charset="utf-8">\n' +157 '<title>' + title + '</title>\n' +158 '</head>\n' +159 '<body>\n' +160 '<pre>' + body + '</pre>\n' +161 '</body>\n' +162 '</html>\n'163}164 165/**166 * Create a directory listener that just 404s.167 * @private168 */169 170function createNotFoundDirectoryListener () {171 return function notFound () {172 this.error(404)173 }174}175 176/**177 * Create a directory listener that performs a redirect.178 * @private179 */180 181function createRedirectDirectoryListener () {182 return function redirect (res) {183 if (this.hasTrailingSlash()) {184 this.error(404)185 return186 }187 188 // get original URL189 var originalUrl = parseUrl.original(this.req)190 191 // append trailing slash192 originalUrl.path = null193 originalUrl.pathname = collapseLeadingSlashes(originalUrl.pathname + '/')194 195 // reformat the URL196 var loc = encodeUrl(url.format(originalUrl))197 var doc = createHtmlDocument('Redirecting', 'Redirecting to ' + escapeHtml(loc))198 199 // send redirect response200 res.statusCode = 301201 res.setHeader('Content-Type', 'text/html; charset=UTF-8')202 res.setHeader('Content-Length', Buffer.byteLength(doc))203 res.setHeader('Content-Security-Policy', "default-src 'none'")204 res.setHeader('X-Content-Type-Options', 'nosniff')205 res.setHeader('Location', loc)206 res.end(doc)207 }208}209 