xprezzo-raw-body
Advanced tools
Comparing version 1.0.3 to 1.0.4
@@ -0,1 +1,6 @@ | ||
1.0.4 / 2020-09-29 | ||
================== | ||
* move HttpRawStream under ./lib and rewrite most functions into private functions. | ||
1.0.3 / 2020-09-27 | ||
@@ -2,0 +7,0 @@ ================== |
228
index.js
@@ -14,14 +14,7 @@ /*! | ||
var bytes = require('bytes') | ||
var debug = require('xprezzo-debug')("xprezzo:rawBody") | ||
var createError = require('xprezzo-http-errors') | ||
var iconv = require('xprezzo-iconv') | ||
var unpipe = require('unpipe') | ||
/** | ||
* Module exports. | ||
* @public | ||
*/ | ||
var bytes = require('bytes'); | ||
var debug = require('xprezzo-debug')("xprezzo:rawBody"); | ||
var HttpRawStream = require('./lib/HttpRawStream'); | ||
/** | ||
* Module exports. | ||
* Get the raw body of a stream (typically HTTP). | ||
@@ -75,7 +68,8 @@ * | ||
// classic callback style | ||
return readStream(stream, encoding, length, limit, done) | ||
//new HttpRawStream(stream, encoding, length, limit, callback); | ||
return new HttpRawStream(stream, encoding, length, limit, done); | ||
} | ||
return new Promise(function executor (resolve, reject) { | ||
readStream(stream, encoding, length, limit, function onRead (err, buf) { | ||
new HttpRawStream(stream, encoding, length, limit, function onRead (err, buf) { | ||
if (err) return reject(err) | ||
@@ -86,209 +80,1 @@ resolve(buf) | ||
} | ||
/** | ||
* 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 (!/^encoding\s+not\s+recognized:\s+/i.test(e.message)) throw e | ||
// the encoding was not found | ||
throw createError(415, 'specified encoding unsupported', { | ||
encoding: encoding, | ||
type: 'encoding.unsupported' | ||
}) | ||
} | ||
} | ||
function HttpRawStream(stream, encoding, length, limit, callback){ | ||
var self = this; | ||
this.complete = false | ||
this.sync = true | ||
this.state = stream._readableState | ||
this.received = 0 | ||
this.decoder = ''; | ||
this.length = length; | ||
this.limit = limit; | ||
this.stream = stream; | ||
this.callback = callback; | ||
this.cleanup=function(){}; | ||
var entitySizeResult = this.checkEntitySize(); | ||
if (entitySizeResult) return entitySizeResult; | ||
var streamEncodingResult = this.checkStreamEncoding(); | ||
if(streamEncodingResult) return streamEncodingResult; | ||
var decoderResult = this.getDecoder(encoding); | ||
if(streamEncodingResult) return streamEncodingResult; | ||
this.setEvent(); | ||
} | ||
HttpRawStream.prototype.done = function () { | ||
var self = this; | ||
var args = new Array(arguments.length); | ||
// copy arguments | ||
for (var i = 0; i < args.length; i++) { | ||
args[i] = arguments[i]; | ||
} | ||
// mark complete | ||
self.complete = true; | ||
if (self.sync) { | ||
process.nextTick(invokeCallback); | ||
} else { | ||
invokeCallback(); | ||
} | ||
function invokeCallback () { | ||
self.cleanup() | ||
if (args[0]) { | ||
debug("callBack"); | ||
// halt the stream on error | ||
self.halt(); | ||
} | ||
self.callback.apply(null, args) | ||
} | ||
return this; | ||
}; | ||
HttpRawStream.prototype.halt = function(){ | ||
// unpipe everything from the stream | ||
unpipe(this.stream); | ||
// pause stream | ||
if (typeof this.stream.pause === 'function') { | ||
this.stream.pause(); | ||
} | ||
}; | ||
HttpRawStream.prototype.checkStreamEncoding=function(){ | ||
// 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 | ||
if (this.stream._decoder || (this.state && (this.state.encoding || this.state.decoder))) { | ||
// developer error | ||
return this.done(createError(500, 'stream encoding should not be set', { | ||
type: 'stream.encoding.set' | ||
})) | ||
} | ||
return false; | ||
} | ||
HttpRawStream.prototype.checkEntitySize = function(){ | ||
var self = this; | ||
// check the length and limit options. | ||
// note: we intentionally leave the stream paused, | ||
// so users should handle the stream themselves. | ||
if (this.limit !== null && this.length !== null && this.length > this.limit) { | ||
return this.done(createError(413, 'request entity too large', { | ||
expected: self.length, | ||
length: self.length, | ||
limit: self.limit, | ||
type: 'entity.too.large' | ||
})) | ||
} | ||
return false; | ||
} | ||
HttpRawStream.prototype.getDecoder = function(encoding) { | ||
try { | ||
this.decoder = getDecoder(encoding) | ||
} catch (err) { | ||
return this.done(err) | ||
} | ||
this.buffer = this.decoder ? '' : [] | ||
return false; | ||
} | ||
HttpRawStream.prototype.setEvent=function(){ | ||
var self = this; | ||
var cleanup = function () { | ||
self.buffer = null; | ||
self.stream.removeListener('aborted', onAborted); | ||
self.stream.removeListener('data', onData); | ||
self.stream.removeListener('end', onEnd); | ||
self.stream.removeListener('error', onEnd); | ||
self.stream.removeListener('close', cleanup); | ||
} | ||
this.cleanup = cleanup; | ||
// attach listeners | ||
self.stream.on('aborted', onAborted); | ||
self.stream.on('close', cleanup); | ||
self.stream.on('data', onData); | ||
self.stream.on('end', onEnd); | ||
self.stream.on('error', onEnd); | ||
// mark sync section complete | ||
this.sync = false; | ||
function onAborted () { | ||
self.onAborted(); | ||
} | ||
function onData (chunk) { | ||
self.onData(chunk); | ||
} | ||
function onEnd (err) { | ||
self.onEnd(err); | ||
} | ||
return this; | ||
}; | ||
HttpRawStream.prototype.onAborted = function () { | ||
var self = this; | ||
if (this.complete) return | ||
this.done(createError(400, 'request aborted', { | ||
code: 'ECONNABORTED', | ||
expected: self.length, | ||
length: self.length, | ||
received: self.received, | ||
type: 'request.aborted' | ||
})); | ||
return false; | ||
}; | ||
HttpRawStream.prototype.onData = function( chunk ){ | ||
var self = this; | ||
if (this.complete) return | ||
this.received += chunk.length | ||
if (this.limit !== null && this.received > this.limit) { | ||
this.done(createError(413, 'request entity too large', { | ||
limit: self.limit, | ||
received: self.received, | ||
type: 'entity.too.large' | ||
})) | ||
} else if (this.decoder) { | ||
this.buffer += this.decoder.write(chunk) | ||
} else { | ||
this.buffer.push(chunk) | ||
} | ||
return false; | ||
}; | ||
HttpRawStream.prototype.onEnd = function(err){ | ||
var self = this; | ||
if (this.complete) return | ||
if (err) return this.done(err) | ||
if (this.length !== null && this.received !== this.length) { | ||
this.done(createError(400, 'request size did not match content length', { | ||
expected: self.length, | ||
length: self.length, | ||
received: self.received, | ||
type: 'request.size.invalid' | ||
})) | ||
} else { | ||
var string = this.decoder | ||
? this.buffer + (this.decoder.end() || '') | ||
: Buffer.concat(this.buffer) | ||
this.done(null, string) | ||
} | ||
return false; | ||
} | ||
/** | ||
* 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) { | ||
new HttpRawStream(stream, encoding, length, limit, callback); | ||
} |
{ | ||
"name": "xprezzo-raw-body", | ||
"description": "Get and validate the raw body of a readable stream.", | ||
"version": "1.0.3", | ||
"version": "1.0.4", | ||
"author": "Leolio Mcleon <info@leolio.page>", | ||
@@ -36,4 +36,4 @@ "license": "MIT", | ||
"README.md", | ||
"index.d.ts", | ||
"index.js" | ||
"index.js", | ||
"lib/" | ||
], | ||
@@ -40,0 +40,0 @@ "scripts": { |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
AI-detected possible typosquat
Supply chain riskAI has identified this package as a potential typosquat of a more popular package. This suggests that the package may be intentionally mimicking another package's name, description, or other metadata.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
16155
6
327
1