| /*! | 
|  * raw-body | 
|  * Copyright(c) 2013-2014 Jonathan Ong | 
|  * Copyright(c) 2014-2022 Douglas Christopher Wilson | 
|  * MIT Licensed | 
|  */ | 
|   | 
| 'use strict' | 
|   | 
| /** | 
|  * Module dependencies. | 
|  * @private | 
|  */ | 
|   | 
| var asyncHooks = tryRequireAsyncHooks() | 
| var bytes = require('bytes') | 
| var createError = require('http-errors') | 
| var iconv = require('iconv-lite') | 
| var unpipe = require('unpipe') | 
|   | 
| /** | 
|  * Module exports. | 
|  * @public | 
|  */ | 
|   | 
| module.exports = getRawBody | 
|   | 
| /** | 
|  * Module variables. | 
|  * @private | 
|  */ | 
|   | 
| var ICONV_ENCODING_MESSAGE_REGEXP = /^Encoding not recognized: / | 
|   | 
| /** | 
|  * Get the decoder for a given encoding. | 
|  * | 
|  * @param {string} encoding | 
|  * @private | 
|  */ | 
|   | 
| function getDecoder (encoding) { | 
|   if (!encoding) return null | 
|   | 
|   try { | 
|     return iconv.getDecoder(encoding) | 
|   } catch (e) { | 
|     // error getting decoder | 
|     if (!ICONV_ENCODING_MESSAGE_REGEXP.test(e.message)) throw e | 
|   | 
|     // the encoding was not found | 
|     throw createError(415, 'specified encoding unsupported', { | 
|       encoding: encoding, | 
|       type: 'encoding.unsupported' | 
|     }) | 
|   } | 
| } | 
|   | 
| /** | 
|  * Get the raw body of a stream (typically HTTP). | 
|  * | 
|  * @param {object} stream | 
|  * @param {object|string|function} [options] | 
|  * @param {function} [callback] | 
|  * @public | 
|  */ | 
|   | 
| function getRawBody (stream, options, callback) { | 
|   var done = callback | 
|   var opts = options || {} | 
|   | 
|   if (options === true || typeof options === 'string') { | 
|     // short cut for encoding | 
|     opts = { | 
|       encoding: options | 
|     } | 
|   } | 
|   | 
|   if (typeof options === 'function') { | 
|     done = options | 
|     opts = {} | 
|   } | 
|   | 
|   // validate callback is a function, if provided | 
|   if (done !== undefined && typeof done !== 'function') { | 
|     throw new TypeError('argument callback must be a function') | 
|   } | 
|   | 
|   // require the callback without promises | 
|   if (!done && !global.Promise) { | 
|     throw new TypeError('argument callback is required') | 
|   } | 
|   | 
|   // get encoding | 
|   var encoding = opts.encoding !== true | 
|     ? opts.encoding | 
|     : 'utf-8' | 
|   | 
|   // convert the limit to an integer | 
|   var limit = bytes.parse(opts.limit) | 
|   | 
|   // convert the expected length to an integer | 
|   var length = opts.length != null && !isNaN(opts.length) | 
|     ? parseInt(opts.length, 10) | 
|     : null | 
|   | 
|   if (done) { | 
|     // classic callback style | 
|     return readStream(stream, encoding, length, limit, wrap(done)) | 
|   } | 
|   | 
|   return new Promise(function executor (resolve, reject) { | 
|     readStream(stream, encoding, length, limit, function onRead (err, buf) { | 
|       if (err) return reject(err) | 
|       resolve(buf) | 
|     }) | 
|   }) | 
| } | 
|   | 
| /** | 
|  * Halt a stream. | 
|  * | 
|  * @param {Object} stream | 
|  * @private | 
|  */ | 
|   | 
| function halt (stream) { | 
|   // unpipe everything from the stream | 
|   unpipe(stream) | 
|   | 
|   // pause stream | 
|   if (typeof stream.pause === 'function') { | 
|     stream.pause() | 
|   } | 
| } | 
|   | 
| /** | 
|  * Read the data from the stream. | 
|  * | 
|  * @param {object} stream | 
|  * @param {string} encoding | 
|  * @param {number} length | 
|  * @param {number} limit | 
|  * @param {function} callback | 
|  * @public | 
|  */ | 
|   | 
| function readStream (stream, encoding, length, limit, callback) { | 
|   var complete = false | 
|   var sync = true | 
|   | 
|   // check the length and limit options. | 
|   // note: we intentionally leave the stream paused, | 
|   // so users should handle the stream themselves. | 
|   if (limit !== null && length !== null && length > limit) { | 
|     return done(createError(413, 'request entity too large', { | 
|       expected: length, | 
|       length: length, | 
|       limit: limit, | 
|       type: 'entity.too.large' | 
|     })) | 
|   } | 
|   | 
|   // streams1: assert request encoding is buffer. | 
|   // streams2+: assert the stream encoding is buffer. | 
|   //   stream._decoder: streams1 | 
|   //   state.encoding: streams2 | 
|   //   state.decoder: streams2, specifically < 0.10.6 | 
|   var state = stream._readableState | 
|   if (stream._decoder || (state && (state.encoding || state.decoder))) { | 
|     // developer error | 
|     return done(createError(500, 'stream encoding should not be set', { | 
|       type: 'stream.encoding.set' | 
|     })) | 
|   } | 
|   | 
|   if (typeof stream.readable !== 'undefined' && !stream.readable) { | 
|     return done(createError(500, 'stream is not readable', { | 
|       type: 'stream.not.readable' | 
|     })) | 
|   } | 
|   | 
|   var received = 0 | 
|   var decoder | 
|   | 
|   try { | 
|     decoder = getDecoder(encoding) | 
|   } catch (err) { | 
|     return done(err) | 
|   } | 
|   | 
|   var buffer = decoder | 
|     ? '' | 
|     : [] | 
|   | 
|   // attach listeners | 
|   stream.on('aborted', onAborted) | 
|   stream.on('close', cleanup) | 
|   stream.on('data', onData) | 
|   stream.on('end', onEnd) | 
|   stream.on('error', onEnd) | 
|   | 
|   // mark sync section complete | 
|   sync = false | 
|   | 
|   function done () { | 
|     var args = new Array(arguments.length) | 
|   | 
|     // copy arguments | 
|     for (var i = 0; i < args.length; i++) { | 
|       args[i] = arguments[i] | 
|     } | 
|   | 
|     // mark complete | 
|     complete = true | 
|   | 
|     if (sync) { | 
|       process.nextTick(invokeCallback) | 
|     } else { | 
|       invokeCallback() | 
|     } | 
|   | 
|     function invokeCallback () { | 
|       cleanup() | 
|   | 
|       if (args[0]) { | 
|         // halt the stream on error | 
|         halt(stream) | 
|       } | 
|   | 
|       callback.apply(null, args) | 
|     } | 
|   } | 
|   | 
|   function onAborted () { | 
|     if (complete) return | 
|   | 
|     done(createError(400, 'request aborted', { | 
|       code: 'ECONNABORTED', | 
|       expected: length, | 
|       length: length, | 
|       received: received, | 
|       type: 'request.aborted' | 
|     })) | 
|   } | 
|   | 
|   function onData (chunk) { | 
|     if (complete) return | 
|   | 
|     received += chunk.length | 
|   | 
|     if (limit !== null && received > limit) { | 
|       done(createError(413, 'request entity too large', { | 
|         limit: limit, | 
|         received: received, | 
|         type: 'entity.too.large' | 
|       })) | 
|     } else if (decoder) { | 
|       buffer += decoder.write(chunk) | 
|     } else { | 
|       buffer.push(chunk) | 
|     } | 
|   } | 
|   | 
|   function onEnd (err) { | 
|     if (complete) return | 
|     if (err) return done(err) | 
|   | 
|     if (length !== null && received !== length) { | 
|       done(createError(400, 'request size did not match content length', { | 
|         expected: length, | 
|         length: length, | 
|         received: received, | 
|         type: 'request.size.invalid' | 
|       })) | 
|     } else { | 
|       var string = decoder | 
|         ? buffer + (decoder.end() || '') | 
|         : Buffer.concat(buffer) | 
|       done(null, string) | 
|     } | 
|   } | 
|   | 
|   function cleanup () { | 
|     buffer = null | 
|   | 
|     stream.removeListener('aborted', onAborted) | 
|     stream.removeListener('data', onData) | 
|     stream.removeListener('end', onEnd) | 
|     stream.removeListener('error', onEnd) | 
|     stream.removeListener('close', cleanup) | 
|   } | 
| } | 
|   | 
| /** | 
|  * Try to require async_hooks | 
|  * @private | 
|  */ | 
|   | 
| function tryRequireAsyncHooks () { | 
|   try { | 
|     return require('async_hooks') | 
|   } catch (e) { | 
|     return {} | 
|   } | 
| } | 
|   | 
| /** | 
|  * Wrap function with async resource, if possible. | 
|  * AsyncResource.bind static method backported. | 
|  * @private | 
|  */ | 
|   | 
| function wrap (fn) { | 
|   var res | 
|   | 
|   // create anonymous resource | 
|   if (asyncHooks.AsyncResource) { | 
|     res = new asyncHooks.AsyncResource(fn.name || 'bound-anonymous-fn') | 
|   } | 
|   | 
|   // incompatible node.js | 
|   if (!res || !res.runInAsyncScope) { | 
|     return fn | 
|   } | 
|   | 
|   // return bound function | 
|   return res.runInAsyncScope.bind(res, fn, null) | 
| } |