This repository has been archived on 2018-10-12. You can view files and clone it, but cannot push or open issues or pull requests.
node-task/node_modules/body-parser/node_modules/raw-body/index.js

225 lines
5.0 KiB
JavaScript

var bytes = require('bytes')
var iconv = require('iconv-lite')
module.exports = function (stream, options, done) {
if (options === true || typeof options === 'string') {
// short cut for encoding
options = {
encoding: options
}
}
options = options || {}
if (typeof options === 'function') {
done = options
options = {}
}
// get encoding
var encoding = options.encoding !== true
? options.encoding
: 'utf-8'
// convert the limit to an integer
var limit = null
if (typeof options.limit === 'number')
limit = options.limit
if (typeof options.limit === 'string')
limit = bytes(options.limit)
// convert the expected length to an integer
var length = null
if (options.length != null && !isNaN(options.length))
length = parseInt(options.length, 10)
// 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) {
var err = makeError('request entity too large', 'entity.too.large')
err.status = err.statusCode = 413
err.length = err.expected = length
err.limit = limit
cleanup()
halt(stream)
process.nextTick(function () {
done(err)
})
return defer
}
// 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
var err = makeError('stream encoding should not be set',
'stream.encoding.set')
err.status = err.statusCode = 500
cleanup()
halt(stream)
process.nextTick(function () {
done(err)
})
return defer
}
var received = 0
var decoder
try {
decoder = getDecoder(encoding)
} catch (err) {
cleanup()
halt(stream)
process.nextTick(function () {
done(err)
})
return defer
}
var buffer = decoder
? ''
: []
stream.on('data', onData)
stream.once('end', onEnd)
stream.once('error', onEnd)
stream.once('close', cleanup)
return defer
// yieldable support
function defer(fn) {
done = fn
}
function onData(chunk) {
received += chunk.length
decoder
? buffer += decoder.write(chunk)
: buffer.push(chunk)
if (limit !== null && received > limit) {
var err = makeError('request entity too large', 'entity.too.large')
err.status = err.statusCode = 413
err.received = received
err.limit = limit
cleanup()
halt(stream)
done(err)
}
}
function onEnd(err) {
if (err) {
cleanup()
halt(stream)
done(err)
} else if (length !== null && received !== length) {
err = makeError('request size did not match content length',
'request.size.invalid')
err.status = err.statusCode = 400
err.received = received
err.length = err.expected = length
cleanup()
done(err)
} else {
var string = decoder
? buffer + (decoder.end() || '')
: Buffer.concat(buffer)
cleanup()
done(null, string)
}
}
function cleanup() {
received = buffer = null
stream.removeListener('data', onData)
stream.removeListener('end', onEnd)
stream.removeListener('error', onEnd)
stream.removeListener('close', cleanup)
}
}
function getDecoder(encoding) {
if (!encoding) return null
try {
return iconv.getCodec(encoding).decoder()
} catch (e) {
var err = makeError('specified encoding unsupported', 'encoding.unsupported')
err.status = err.statusCode = 415
err.encoding = encoding
throw err
}
}
/**
* Halt a stream.
*
* @param {Object} stream
* @api private
*/
function halt(stream) {
// unpipe everything from the stream
unpipe(stream)
// pause stream
if (typeof stream.pause === 'function') {
stream.pause()
}
}
// to create serializable errors you must re-set message so
// that it is enumerable and you must re configure the type
// property so that is writable and enumerable
function makeError(message, type) {
var error = new Error()
error.message = message
Object.defineProperty(error, 'type', {
value: type,
enumerable: true,
writable: true,
configurable: true
})
return error
}
/**
* Unpipe everything from a stream.
*
* @param {Object} stream
* @api private
*/
/* istanbul ignore next: implementation differs between versions */
function unpipe(stream) {
if (typeof stream.unpipe === 'function') {
// new-style
stream.unpipe()
return
}
// Node.js 0.8 hack
var listener
var listeners = stream.listeners('close')
for (var i = 0; i < listeners.length; i++) {
listener = listeners[i]
if (listener.name !== 'cleanup' && listener.name !== 'onclose') {
continue
}
// invoke the listener
listener.call(stream)
}
}