The LM Control website. Simple yet efficient.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

index.js 4.5KB

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