FileController.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. const FileHandler = require('./FileHandler')
  2. const metrics = require('@overleaf/metrics')
  3. const parseRange = require('range-parser')
  4. const Errors = require('./Errors')
  5. const { pipeline } = require('node:stream')
  6. const maxSizeInBytes = 1024 * 1024 * 1024 // 1GB
  7. module.exports = {
  8. getFile,
  9. getFileHead,
  10. insertFile,
  11. }
  12. function getFile(req, res, next) {
  13. const { key, bucket } = req
  14. const { format, style } = req.query
  15. const options = {
  16. key,
  17. bucket,
  18. format,
  19. style,
  20. }
  21. if (req.useSubdirectories) options.useSubdirectories = true
  22. metrics.inc('getFile')
  23. req.requestLogger.setMessage('getting file')
  24. req.requestLogger.addFields({
  25. key,
  26. bucket,
  27. format,
  28. style,
  29. cacheWarm: req.query.cacheWarm,
  30. })
  31. if (req.headers.range) {
  32. const range = _getRange(req.headers.range)
  33. if (range) {
  34. options.start = range.start
  35. options.end = range.end
  36. req.requestLogger.addFields({ range })
  37. }
  38. }
  39. FileHandler.getRedirectUrl(bucket, key, options, function (err, redirectUrl) {
  40. if (err) {
  41. metrics.inc('file_redirect_error')
  42. }
  43. if (redirectUrl) {
  44. metrics.inc('file_redirect')
  45. return res.redirect(redirectUrl)
  46. }
  47. FileHandler.getFile(bucket, key, options, function (err, fileStream) {
  48. if (err) {
  49. if (err instanceof Errors.NotFoundError) {
  50. res.sendStatus(404)
  51. } else {
  52. next(err)
  53. }
  54. return
  55. }
  56. if (req.query.cacheWarm) {
  57. fileStream.destroy()
  58. return res.sendStatus(200).end()
  59. }
  60. pipeline(fileStream, res, err => {
  61. if (err && err.code === 'ERR_STREAM_PREMATURE_CLOSE') {
  62. res.end()
  63. } else if (err) {
  64. next(
  65. new Errors.ReadError(
  66. 'error transferring stream',
  67. { bucket, key, format, style },
  68. err
  69. )
  70. )
  71. }
  72. })
  73. })
  74. })
  75. }
  76. function getFileHead(req, res, next) {
  77. const { key, bucket } = req
  78. metrics.inc('getFileSize')
  79. req.requestLogger.setMessage('getting file size')
  80. req.requestLogger.addFields({ key, bucket })
  81. FileHandler.getFileSize(bucket, key, function (err, fileSize) {
  82. if (err) {
  83. if (err instanceof Errors.NotFoundError) {
  84. res.sendStatus(404)
  85. } else {
  86. next(err)
  87. }
  88. return
  89. }
  90. res.set('Content-Length', fileSize)
  91. res.status(200).end()
  92. })
  93. }
  94. function insertFile(req, res, next) {
  95. metrics.inc('insertFile')
  96. const { key, bucket } = req
  97. req.requestLogger.setMessage('inserting file')
  98. req.requestLogger.addFields({ key, bucket })
  99. FileHandler.insertFile(bucket, key, req, function (err) {
  100. if (err) {
  101. next(err)
  102. } else {
  103. res.sendStatus(200)
  104. }
  105. })
  106. }
  107. function _getRange(header) {
  108. const parsed = parseRange(maxSizeInBytes, header)
  109. if (parsed === -1 || parsed === -2 || parsed.type !== 'bytes') {
  110. return null
  111. } else {
  112. const range = parsed[0]
  113. return { start: range.start, end: range.end }
  114. }
  115. }