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 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /*!
  2. * raw-body
  3. * Copyright(c) 2013-2014 Jonathan Ong
  4. * Copyright(c) 2014-2015 Douglas Christopher Wilson
  5. * MIT Licensed
  6. */
  7. 'use strict'
  8. /**
  9. * Module dependencies.
  10. * @private
  11. */
  12. var bytes = require('bytes')
  13. var createError = require('http-errors')
  14. var iconv = require('iconv-lite')
  15. var unpipe = require('unpipe')
  16. /**
  17. * Module exports.
  18. * @public
  19. */
  20. module.exports = getRawBody
  21. /**
  22. * Module variables.
  23. * @private
  24. */
  25. var ICONV_ENCODING_MESSAGE_REGEXP = /^Encoding not recognized: /
  26. /**
  27. * Get the decoder for a given encoding.
  28. *
  29. * @param {string} encoding
  30. * @private
  31. */
  32. function getDecoder (encoding) {
  33. if (!encoding) return null
  34. try {
  35. return iconv.getDecoder(encoding)
  36. } catch (e) {
  37. // error getting decoder
  38. if (!ICONV_ENCODING_MESSAGE_REGEXP.test(e.message)) throw e
  39. // the encoding was not found
  40. throw createError(415, 'specified encoding unsupported', {
  41. encoding: encoding,
  42. type: 'encoding.unsupported'
  43. })
  44. }
  45. }
  46. /**
  47. * Get the raw body of a stream (typically HTTP).
  48. *
  49. * @param {object} stream
  50. * @param {object|string|function} [options]
  51. * @param {function} [callback]
  52. * @public
  53. */
  54. function getRawBody (stream, options, callback) {
  55. var done = callback
  56. var opts = options || {}
  57. if (options === true || typeof options === 'string') {
  58. // short cut for encoding
  59. opts = {
  60. encoding: options
  61. }
  62. }
  63. if (typeof options === 'function') {
  64. done = options
  65. opts = {}
  66. }
  67. // validate callback is a function, if provided
  68. if (done !== undefined && typeof done !== 'function') {
  69. throw new TypeError('argument callback must be a function')
  70. }
  71. // require the callback without promises
  72. if (!done && !global.Promise) {
  73. throw new TypeError('argument callback is required')
  74. }
  75. // get encoding
  76. var encoding = opts.encoding !== true
  77. ? opts.encoding
  78. : 'utf-8'
  79. // convert the limit to an integer
  80. var limit = bytes.parse(opts.limit)
  81. // convert the expected length to an integer
  82. var length = opts.length != null && !isNaN(opts.length)
  83. ? parseInt(opts.length, 10)
  84. : null
  85. if (done) {
  86. // classic callback style
  87. return readStream(stream, encoding, length, limit, done)
  88. }
  89. return new Promise(function executor (resolve, reject) {
  90. readStream(stream, encoding, length, limit, function onRead (err, buf) {
  91. if (err) return reject(err)
  92. resolve(buf)
  93. })
  94. })
  95. }
  96. /**
  97. * Halt a stream.
  98. *
  99. * @param {Object} stream
  100. * @private
  101. */
  102. function halt (stream) {
  103. // unpipe everything from the stream
  104. unpipe(stream)
  105. // pause stream
  106. if (typeof stream.pause === 'function') {
  107. stream.pause()
  108. }
  109. }
  110. /**
  111. * Read the data from the stream.
  112. *
  113. * @param {object} stream
  114. * @param {string} encoding
  115. * @param {number} length
  116. * @param {number} limit
  117. * @param {function} callback
  118. * @public
  119. */
  120. function readStream (stream, encoding, length, limit, callback) {
  121. var complete = false
  122. var sync = true
  123. // check the length and limit options.
  124. // note: we intentionally leave the stream paused,
  125. // so users should handle the stream themselves.
  126. if (limit !== null && length !== null && length > limit) {
  127. return done(createError(413, 'request entity too large', {
  128. expected: length,
  129. length: length,
  130. limit: limit,
  131. type: 'entity.too.large'
  132. }))
  133. }
  134. // streams1: assert request encoding is buffer.
  135. // streams2+: assert the stream encoding is buffer.
  136. // stream._decoder: streams1
  137. // state.encoding: streams2
  138. // state.decoder: streams2, specifically < 0.10.6
  139. var state = stream._readableState
  140. if (stream._decoder || (state && (state.encoding || state.decoder))) {
  141. // developer error
  142. return done(createError(500, 'stream encoding should not be set', {
  143. type: 'stream.encoding.set'
  144. }))
  145. }
  146. var received = 0
  147. var decoder
  148. try {
  149. decoder = getDecoder(encoding)
  150. } catch (err) {
  151. return done(err)
  152. }
  153. var buffer = decoder
  154. ? ''
  155. : []
  156. // attach listeners
  157. stream.on('aborted', onAborted)
  158. stream.on('close', cleanup)
  159. stream.on('data', onData)
  160. stream.on('end', onEnd)
  161. stream.on('error', onEnd)
  162. // mark sync section complete
  163. sync = false
  164. function done () {
  165. var args = new Array(arguments.length)
  166. // copy arguments
  167. for (var i = 0; i < args.length; i++) {
  168. args[i] = arguments[i]
  169. }
  170. // mark complete
  171. complete = true
  172. if (sync) {
  173. process.nextTick(invokeCallback)
  174. } else {
  175. invokeCallback()
  176. }
  177. function invokeCallback () {
  178. cleanup()
  179. if (args[0]) {
  180. // halt the stream on error
  181. halt(stream)
  182. }
  183. callback.apply(null, args)
  184. }
  185. }
  186. function onAborted () {
  187. if (complete) return
  188. done(createError(400, 'request aborted', {
  189. code: 'ECONNABORTED',
  190. expected: length,
  191. length: length,
  192. received: received,
  193. type: 'request.aborted'
  194. }))
  195. }
  196. function onData (chunk) {
  197. if (complete) return
  198. received += chunk.length
  199. if (limit !== null && received > limit) {
  200. done(createError(413, 'request entity too large', {
  201. limit: limit,
  202. received: received,
  203. type: 'entity.too.large'
  204. }))
  205. } else if (decoder) {
  206. buffer += decoder.write(chunk)
  207. } else {
  208. buffer.push(chunk)
  209. }
  210. }
  211. function onEnd (err) {
  212. if (complete) return
  213. if (err) return done(err)
  214. if (length !== null && received !== length) {
  215. done(createError(400, 'request size did not match content length', {
  216. expected: length,
  217. length: length,
  218. received: received,
  219. type: 'request.size.invalid'
  220. }))
  221. } else {
  222. var string = decoder
  223. ? buffer + (decoder.end() || '')
  224. : Buffer.concat(buffer)
  225. done(null, string)
  226. }
  227. }
  228. function cleanup () {
  229. buffer = null
  230. stream.removeListener('aborted', onAborted)
  231. stream.removeListener('data', onData)
  232. stream.removeListener('end', onEnd)
  233. stream.removeListener('error', onEnd)
  234. stream.removeListener('close', cleanup)
  235. }
  236. }