index.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. var bytes = require('bytes')
  2. // NOTE: the trailing slash is not a typo
  3. var StringDecoder = require('string_decoder/').StringDecoder
  4. module.exports = function (stream, options, done) {
  5. if (typeof options === 'function') {
  6. done = options
  7. options = {}
  8. } else if (!options) {
  9. options = {}
  10. } else if (options === true) {
  11. options = {
  12. encoding: 'utf8'
  13. }
  14. }
  15. // convert the limit to an integer
  16. var limit = null
  17. if (typeof options.limit === 'number')
  18. limit = options.limit
  19. if (typeof options.limit === 'string')
  20. limit = bytes(options.limit)
  21. // convert the expected length to an integer
  22. var length = null
  23. if (options.length != null && !isNaN(options.length))
  24. length = parseInt(options.length, 10)
  25. // check the length and limit options.
  26. // note: we intentionally leave the stream paused,
  27. // so users should handle the stream themselves.
  28. if (limit !== null && length !== null && length > limit) {
  29. if (typeof stream.pause === 'function')
  30. stream.pause()
  31. process.nextTick(function () {
  32. var err = makeError('request entity too large', 'entity.too.large')
  33. err.status = err.statusCode = 413
  34. err.length = err.expected = length
  35. err.limit = limit
  36. done(err)
  37. })
  38. return defer
  39. }
  40. // streams1: assert request encoding is buffer.
  41. // streams2+: assert the stream encoding is buffer.
  42. // stream._decoder: streams1
  43. // state.encoding: streams2
  44. // state.decoder: streams2, specifically < 0.10.6
  45. var state = stream._readableState
  46. if (stream._decoder || (state && (state.encoding || state.decoder))) {
  47. if (typeof stream.pause === 'function')
  48. stream.pause()
  49. process.nextTick(function () {
  50. var err = makeError('stream encoding should not be set',
  51. 'stream.encoding.set')
  52. // developer error
  53. err.status = err.statusCode = 500
  54. done(err)
  55. })
  56. return defer
  57. }
  58. var received = 0
  59. // note: we delegate any invalid encodings to the constructor
  60. var decoder = options.encoding
  61. ? new StringDecoder(options.encoding === true ? 'utf8' : options.encoding)
  62. : null
  63. var buffer = decoder
  64. ? ''
  65. : []
  66. stream.on('data', onData)
  67. stream.once('end', onEnd)
  68. stream.once('error', onEnd)
  69. stream.once('close', cleanup)
  70. return defer
  71. // yieldable support
  72. function defer(fn) {
  73. done = fn
  74. }
  75. function onData(chunk) {
  76. received += chunk.length
  77. decoder
  78. ? buffer += decoder.write(chunk)
  79. : buffer.push(chunk)
  80. if (limit !== null && received > limit) {
  81. if (typeof stream.pause === 'function')
  82. stream.pause()
  83. var err = makeError('request entity too large', 'entity.too.large')
  84. err.status = err.statusCode = 413
  85. err.received = received
  86. err.limit = limit
  87. done(err)
  88. cleanup()
  89. }
  90. }
  91. function onEnd(err) {
  92. if (err) {
  93. if (typeof stream.pause === 'function')
  94. stream.pause()
  95. done(err)
  96. } else if (length !== null && received !== length) {
  97. err = makeError('request size did not match content length',
  98. 'request.size.invalid')
  99. err.status = err.statusCode = 400
  100. err.received = received
  101. err.length = err.expected = length
  102. done(err)
  103. } else {
  104. done(null, decoder
  105. ? buffer + decoder.end()
  106. : Buffer.concat(buffer)
  107. )
  108. }
  109. cleanup()
  110. }
  111. function cleanup() {
  112. received = buffer = null
  113. stream.removeListener('data', onData)
  114. stream.removeListener('end', onEnd)
  115. stream.removeListener('error', onEnd)
  116. stream.removeListener('close', cleanup)
  117. }
  118. }
  119. // to create serializable errors you must re-set message so
  120. // that it is enumerable and you must re configure the type
  121. // property so that is writable and enumerable
  122. function makeError(message, type) {
  123. var error = new Error()
  124. error.message = message
  125. Object.defineProperty(error, 'type', {
  126. value: type,
  127. enumerable: true,
  128. writable: true,
  129. configurable: true
  130. })
  131. return error
  132. }