1 var bytes = require('bytes')
2 var iconv = require('iconv-lite')
4 module.exports = function (stream, options, done) {
5 if (options === true || typeof options === 'string') {
6 // short cut for encoding
12 options = options || {}
14 if (typeof options === 'function') {
20 var encoding = options.encoding !== true
24 // convert the limit to an integer
26 if (typeof options.limit === 'number')
28 if (typeof options.limit === 'string')
29 limit = bytes(options.limit)
31 // convert the expected length to an integer
33 if (options.length != null && !isNaN(options.length))
34 length = parseInt(options.length, 10)
36 // check the length and limit options.
37 // note: we intentionally leave the stream paused,
38 // so users should handle the stream themselves.
39 if (limit !== null && length !== null && length > limit) {
40 var err = makeError('request entity too large', 'entity.too.large')
41 err.status = err.statusCode = 413
42 err.length = err.expected = length
46 process.nextTick(function () {
52 // streams1: assert request encoding is buffer.
53 // streams2+: assert the stream encoding is buffer.
54 // stream._decoder: streams1
55 // state.encoding: streams2
56 // state.decoder: streams2, specifically < 0.10.6
57 var state = stream._readableState
58 if (stream._decoder || (state && (state.encoding || state.decoder))) {
60 var err = makeError('stream encoding should not be set',
61 'stream.encoding.set')
62 err.status = err.statusCode = 500
65 process.nextTick(function () {
75 decoder = getDecoder(encoding)
79 process.nextTick(function () {
89 stream.on('data', onData)
90 stream.once('end', onEnd)
91 stream.once('error', onEnd)
92 stream.once('close', cleanup)
101 function onData(chunk) {
102 received += chunk.length
104 ? buffer += decoder.write(chunk)
107 if (limit !== null && received > limit) {
108 var err = makeError('request entity too large', 'entity.too.large')
109 err.status = err.statusCode = 413
110 err.received = received
118 function onEnd(err) {
123 } else if (length !== null && received !== length) {
124 err = makeError('request size did not match content length',
125 'request.size.invalid')
126 err.status = err.statusCode = 400
127 err.received = received
128 err.length = err.expected = length
133 ? buffer + (decoder.end() || '')
134 : Buffer.concat(buffer)
141 received = buffer = null
143 stream.removeListener('data', onData)
144 stream.removeListener('end', onEnd)
145 stream.removeListener('error', onEnd)
146 stream.removeListener('close', cleanup)
150 function getDecoder(encoding) {
151 if (!encoding) return null
154 return iconv.getCodec(encoding).decoder()
156 var err = makeError('specified encoding unsupported', 'encoding.unsupported')
157 err.status = err.statusCode = 415
158 err.encoding = encoding
166 * @param {Object} stream
170 function halt(stream) {
171 // unpipe everything from the stream
175 if (typeof stream.pause === 'function') {
180 // to create serializable errors you must re-set message so
181 // that it is enumerable and you must re configure the type
182 // property so that is writable and enumerable
183 function makeError(message, type) {
184 var error = new Error()
185 error.message = message
186 Object.defineProperty(error, 'type', {
196 * Unpipe everything from a stream.
198 * @param {Object} stream
202 /* istanbul ignore next: implementation differs between versions */
203 function unpipe(stream) {
204 if (typeof stream.unpipe === 'function') {
212 var listeners = stream.listeners('close')
214 for (var i = 0; i < listeners.length; i++) {
215 listener = listeners[i]
217 if (listener.name !== 'cleanup' && listener.name !== 'onclose') {
221 // invoke the listener
222 listener.call(stream)