common.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. /**
  2. * This module contains some common helpers shared between middlewares
  3. */
  4. var mime = require('mime')
  5. var _ = require('lodash')
  6. var parseRange = require('range-parser')
  7. var Buffer = require('safe-buffer').Buffer
  8. var log = require('../logger').create('web-server')
  9. var PromiseContainer = function () {
  10. var promise
  11. this.then = function (success, error) {
  12. error = error || _.noop
  13. return promise.then(success).catch(error)
  14. }
  15. this.set = function (newPromise) {
  16. promise = newPromise
  17. }
  18. }
  19. var serve404 = function (response, path) {
  20. log.warn('404: ' + path)
  21. response.writeHead(404)
  22. return response.end('NOT FOUND')
  23. }
  24. var createServeFile = function (fs, directory, config) {
  25. var cache = Object.create(null)
  26. return function (filepath, rangeHeader, response, transform, content, doNotCache) {
  27. var responseData
  28. var convertForRangeRequest = function () {
  29. var range = parseRange(responseData.length, rangeHeader)
  30. if (range === -2) {
  31. // malformed header string
  32. return 200
  33. } else if (range === -1) {
  34. // unsatisfiable range
  35. responseData = Buffer.alloc(0)
  36. return 416
  37. } else if (range.type === 'bytes') {
  38. responseData = Buffer.from(responseData)
  39. if (range.length === 1) {
  40. var start = range[0].start
  41. var end = range[0].end
  42. response.setHeader(
  43. 'Content-Range',
  44. 'bytes ' + start + '-' + end + '/' + responseData.length
  45. )
  46. response.setHeader('Accept-Ranges', 'bytes')
  47. response.setHeader('Content-Length', end - start + 1)
  48. responseData = responseData.slice(start, end + 1)
  49. return 206
  50. } else {
  51. // Multiple ranges are not supported. Maybe future?
  52. responseData = Buffer.alloc(0)
  53. return 416
  54. }
  55. }
  56. // All other states, ignore
  57. return 200
  58. }
  59. if (directory) {
  60. filepath = directory + filepath
  61. }
  62. if (!content && cache[filepath]) {
  63. content = cache[filepath]
  64. }
  65. if (config && config.customHeaders && config.customHeaders.length > 0) {
  66. config.customHeaders.forEach(function (header) {
  67. var regex = new RegExp(header.match)
  68. if (regex.test(filepath)) {
  69. log.debug('setting header: ' + header.name + ' for: ' + filepath)
  70. response.setHeader(header.name, header.value)
  71. }
  72. })
  73. }
  74. // serve from cache
  75. if (content && !doNotCache) {
  76. response.setHeader('Content-Type', mime.lookup(filepath, 'text/plain'))
  77. // call custom transform fn to transform the data
  78. responseData = (transform && transform(content)) || content
  79. if (rangeHeader) {
  80. var code = convertForRangeRequest()
  81. response.writeHead(code)
  82. } else {
  83. response.writeHead(200)
  84. }
  85. log.debug('serving (cached): ' + filepath)
  86. return response.end(responseData)
  87. }
  88. return fs.readFile(filepath, function (error, data) {
  89. if (error) {
  90. return serve404(response, filepath)
  91. }
  92. if (!doNotCache) {
  93. cache[filepath] = data.toString()
  94. }
  95. response.setHeader('Content-Type', mime.lookup(filepath, 'text/plain'))
  96. // call custom transform fn to transform the data
  97. responseData = (transform && transform(data.toString())) || data
  98. if (rangeHeader) {
  99. var code = convertForRangeRequest()
  100. response.writeHead(code)
  101. } else {
  102. response.writeHead(200)
  103. }
  104. log.debug('serving: ' + filepath)
  105. return response.end(responseData)
  106. })
  107. }
  108. }
  109. var setNoCacheHeaders = function (response) {
  110. response.setHeader('Cache-Control', 'no-cache')
  111. response.setHeader('Pragma', 'no-cache')
  112. response.setHeader('Expires', (new Date(0)).toUTCString())
  113. }
  114. var setHeavyCacheHeaders = function (response) {
  115. response.setHeader('Cache-Control', 'public, max-age=31536000')
  116. }
  117. var initializeMimeTypes = function (config) {
  118. if (config && config.mime) {
  119. _.forEach(config.mime, function (value, key) {
  120. var map = {}
  121. map[key] = value
  122. mime.define(map)
  123. })
  124. }
  125. }
  126. // PUBLIC API
  127. exports.PromiseContainer = PromiseContainer
  128. exports.createServeFile = createServeFile
  129. exports.setNoCacheHeaders = setNoCacheHeaders
  130. exports.setHeavyCacheHeaders = setHeavyCacheHeaders
  131. exports.initializeMimeTypes = initializeMimeTypes
  132. exports.serve404 = serve404