xprezzo-raw-body
Advanced tools
Comparing version 1.0.6 to 1.0.7
@@ -0,1 +1,6 @@ | ||
1.0.7 / 2020-10-02 | ||
================== | ||
* Rewrite HttpRawStream as ES6 class | ||
1.0.6 / 2020-09-29 | ||
@@ -2,0 +7,0 @@ ================== |
89
index.js
@@ -14,11 +14,10 @@ /*! | ||
var bytes = require('bytes'); | ||
var debug = require('xprezzo-debug')("xprezzo:rawBody"); | ||
var HttpRawStream = require('./lib/HttpRawStream'); | ||
var createError = require('xprezzo-http-errors') | ||
var iconv = require('xprezzo-iconv') | ||
var onFinished = require('xprezzo-on-finished') | ||
var zlib = require('zlib') | ||
const bytes = require('bytes') | ||
const HttpRawStream = require('./lib/HttpRawStream') | ||
const createError = require('xprezzo-http-errors') | ||
const iconv = require('xprezzo-iconv') | ||
const onFinished = require('xprezzo-on-finished') | ||
const zlib = require('zlib') | ||
/** | ||
/** | ||
* Module exports. | ||
@@ -32,5 +31,5 @@ * Wrap and protect the HttpRawStream | ||
*/ | ||
var getBody = module.exports = function (stream, options, callback) { | ||
var done = callback | ||
var opts = options || {} | ||
const getBody = module.exports = (stream, options, callback) => { | ||
let done = callback | ||
let opts = options || {} | ||
@@ -42,5 +41,3 @@ if (options === true || typeof options === 'string') { | ||
} | ||
} | ||
if (typeof options === 'function') { | ||
} else if (typeof options === 'function') { | ||
done = options | ||
@@ -61,3 +58,3 @@ opts = {} | ||
// get encoding | ||
var encoding = opts.encoding !== true | ||
const encoding = opts.encoding !== true | ||
? opts.encoding | ||
@@ -67,6 +64,6 @@ : 'utf-8' | ||
// convert the limit to an integer | ||
var limit = bytes.parse(opts.limit) | ||
const limit = bytes.parse(opts.limit) | ||
// convert the expected length to an integer | ||
var length = opts.length != null && !isNaN(opts.length) | ||
const length = opts.length != null && !isNaN(opts.length) | ||
? parseInt(opts.length, 10) | ||
@@ -77,10 +74,23 @@ : null | ||
// classic callback style | ||
//new HttpRawStream(stream, encoding, length, limit, callback); | ||
return new HttpRawStream(stream, encoding, length, limit, done); | ||
// new HttpRawStream(stream, encoding, length, limit, callback); | ||
return new HttpRawStream({ | ||
stream: stream, | ||
encoding: encoding, | ||
length: length, | ||
limit: limit, | ||
callback: done | ||
}) | ||
} | ||
return new Promise(function executor (resolve, reject) { | ||
new HttpRawStream(stream, encoding, length, limit, function onRead (err, buf) { | ||
if (err) return reject(err) | ||
resolve(buf) | ||
return new Promise((resolve, reject) => { | ||
/* eslint-disable no-new */ | ||
new HttpRawStream({ | ||
stream: stream, | ||
encoding: encoding, | ||
length: length, | ||
limit: limit, | ||
callback: (err, buf) => { | ||
if (err) return reject(err) | ||
resolve(buf) | ||
} | ||
}) | ||
@@ -92,5 +102,5 @@ }) | ||
* Module exports. | ||
* | ||
* | ||
* Reader | ||
* | ||
* | ||
* Read a request into a buffer and parse. | ||
@@ -106,6 +116,6 @@ * | ||
*/ | ||
module.exports.Reader = function (req, res, next, parse, debug, options) { | ||
var length | ||
var opts = options | ||
var stream | ||
module.exports.Reader = (req, res, next, parse, debug, options) => { | ||
let length | ||
const opts = options | ||
let stream | ||
@@ -116,6 +126,6 @@ // flag as parsed | ||
// read options | ||
var encoding = opts.encoding !== null | ||
const encoding = opts.encoding !== null | ||
? opts.encoding | ||
: null | ||
var verify = opts.verify | ||
const verify = opts.verify | ||
debug('typeof verify = ' + typeof verify) | ||
@@ -151,5 +161,5 @@ | ||
debug('read body') | ||
getBody(stream, opts, function (error, body) { | ||
getBody(stream, opts, (error, body) => { | ||
if (error) { | ||
var _error | ||
let _error | ||
@@ -190,3 +200,3 @@ if (error.type === 'encoding.unsupported') { | ||
// parse | ||
var str = body | ||
let str = body | ||
try { | ||
@@ -205,3 +215,2 @@ debug('parse body') | ||
} | ||
next() | ||
@@ -221,6 +230,6 @@ }) | ||
function contentstream (req, debug, inflate) { | ||
var encoding = (req.headers['content-encoding'] || 'identity').toLowerCase() | ||
var length = req.headers['content-length'] | ||
var stream | ||
const contentstream = (req, debug, inflate) => { | ||
const encoding = (req.headers['content-encoding'] || 'identity').toLowerCase() | ||
const length = req.headers['content-length'] | ||
let stream | ||
@@ -262,3 +271,3 @@ debug('content-encoding "%s"', encoding) | ||
* Module exports. | ||
* | ||
* | ||
*/ | ||
@@ -265,0 +274,0 @@ module.exports.bytes = bytes |
@@ -14,55 +14,29 @@ /*! | ||
var debug = require('xprezzo-debug')("xprezzo:HttpRawStream"); | ||
var createError = require('xprezzo-http-errors'); | ||
var iconv = require('xprezzo-iconv'); | ||
var unpipe = require('unpipe'); | ||
const debug = require('xprezzo-debug')('xprezzo:HttpRawStream') | ||
const createError = require('xprezzo-http-errors') | ||
const iconv = require('xprezzo-iconv') | ||
const unpipe = require('unpipe') | ||
const prop = new WeakMap() | ||
class HttpRawStream { | ||
constructor (options) { | ||
const opts = options || {} | ||
opts.complete = false | ||
opts.sync = true | ||
opts.state = opts.stream._readableState | ||
opts.received = 0 | ||
opts.decoder = '' | ||
this.limit2 = opts.limit | ||
this.cleanup = function () {} | ||
prop.set(this, opts) | ||
const entitySizeResult = checkEntitySize.call(this) | ||
if (entitySizeResult) return entitySizeResult | ||
const streamEncodingResult = checkStreamEncoding.call(this) | ||
if (streamEncodingResult) return streamEncodingResult | ||
const decoderResult = useDecoder.call(this, opts.encoding) | ||
if (decoderResult) return decoderResult | ||
setListeners.call(this) | ||
} | ||
} | ||
/** | ||
* Private class variables | ||
* @private | ||
*/ | ||
var count=0; | ||
/** | ||
* Module exports. | ||
* Http Raw stream for reading data. | ||
* | ||
* @param {object} stream | ||
* @param {string} encoding | ||
* @param {number} length | ||
* @param {number} limit | ||
* @param {function} callback | ||
* @public | ||
*/ | ||
module.exports = (function(){ | ||
/** | ||
* Private class variables | ||
* @private | ||
*/ | ||
var count2=0; | ||
return function (stream, encoding, length, limit, callback){ | ||
debug("Object Count : "+ ++count); | ||
debug("Object Count2 : "+ ++count2); | ||
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 = checkEntitySize.call(this); | ||
if (entitySizeResult) return entitySizeResult; | ||
var streamEncodingResult = checkStreamEncoding.call(this); | ||
if(streamEncodingResult) return streamEncodingResult; | ||
var decoderResult = useDecoder.call(this, encoding); | ||
if(decoderResult) return decoderResult; | ||
setListeners.call(this); | ||
} | ||
})(); | ||
/** | ||
* Get the decoder for a given encoding. | ||
@@ -73,6 +47,4 @@ * | ||
*/ | ||
function getDecoder (encoding) { | ||
const getDecoder = (encoding) => { | ||
if (!encoding) return null | ||
try { | ||
@@ -97,4 +69,5 @@ return iconv.getDecoder(encoding) | ||
*/ | ||
function checkStreamEncoding(){ | ||
// Avoid using arrow function expression to prevent incorrect "this" | ||
function checkStreamEncoding () { | ||
const self = prop.get(this) | ||
// streams1: assert request encoding is buffer. | ||
@@ -105,9 +78,9 @@ // streams2+: assert the stream encoding is buffer. | ||
// state.decoder: streams2, specifically < 0.10.6 | ||
if (this.stream._decoder || (this.state && (this.state.encoding || this.state.decoder))) { | ||
if (self.stream._decoder || (self.state && (self.state.encoding || self.state.decoder))) { | ||
// developer error | ||
return final.call(this,createError(500, 'stream encoding should not be set', { | ||
return final.call(this, createError(500, 'stream encoding should not be set', { | ||
type: 'stream.encoding.set' | ||
})) | ||
} | ||
return false; | ||
return false | ||
} | ||
@@ -120,28 +93,30 @@ | ||
*/ | ||
function final() { | ||
var self = this; | ||
var args = new Array(arguments.length); | ||
// Avoid using arrow function expression to prevent incorrect "this" | ||
function final () { | ||
const self = prop.get(this) | ||
const that = this | ||
const args = new Array(arguments.length) | ||
// copy arguments | ||
for (var i = 0; i < args.length; i++) { | ||
args[i] = arguments[i]; | ||
for (let 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() | ||
self.complete = true | ||
prop.set(this, self) | ||
const invokeCallback = () => { | ||
that.cleanup() | ||
if (args[0]) { | ||
debug("callBack"); | ||
debug('callBack') | ||
// halt the stream on error | ||
halt.call(self); | ||
halt.call(that) | ||
} | ||
self.callback.apply(null, args) | ||
} | ||
return this; | ||
}; | ||
if (self.sync) { | ||
process.nextTick(invokeCallback) | ||
} else { | ||
invokeCallback() | ||
} | ||
return this | ||
} | ||
@@ -153,11 +128,12 @@ /** | ||
*/ | ||
function halt(){ | ||
// Avoid using arrow function expression to prevent incorrect "this" | ||
function halt () { | ||
const self = prop.get(this) | ||
// unpipe everything from the stream | ||
unpipe(this.stream); | ||
unpipe(self.stream) | ||
// pause stream | ||
if (typeof this.stream.pause === 'function') { | ||
this.stream.pause(); | ||
if (typeof self.stream.pause === 'function') { | ||
self.stream.pause() | ||
} | ||
}; | ||
} | ||
@@ -169,10 +145,10 @@ /** | ||
*/ | ||
function checkEntitySize(){ | ||
var self = this; | ||
// Avoid using arrow function expression to prevent incorrect "this" | ||
function checkEntitySize () { | ||
const self = prop.get(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 final.call(this,createError(413, 'request entity too large', { | ||
if (self.limit !== null && self.length !== null && self.length > self.limit) { | ||
return final.call(this, createError(413, 'request entity too large', { | ||
expected: self.length, | ||
@@ -184,3 +160,3 @@ length: self.length, | ||
} | ||
return false; | ||
return false | ||
} | ||
@@ -195,11 +171,13 @@ | ||
*/ | ||
function useDecoder(encoding) { | ||
// Avoid using arrow function expression to prevent incorrect "this" | ||
function useDecoder (encoding) { | ||
const self = prop.get(this) | ||
try { | ||
this.decoder = getDecoder(encoding) | ||
self.decoder = getDecoder(encoding) | ||
} catch (err) { | ||
return final.call(this,err) | ||
return final.call(this, err) | ||
} | ||
this.buffer = this.decoder ? '' : [] | ||
return false; | ||
self.buffer = self.decoder ? '' : [] | ||
prop.set(this, self) | ||
return false | ||
} | ||
@@ -213,33 +191,38 @@ | ||
*/ | ||
function setListeners(){ | ||
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); | ||
// Avoid using arrow function expression to prevent incorrect "this" | ||
function setListeners () { | ||
const self = prop.get(this) | ||
debug('3. limit:' + self.limit) | ||
const that = this | ||
function cleanup () { | ||
self.buffer = null | ||
prop.set(that, self) | ||
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; | ||
this.cleanup = cleanup | ||
prop.set(this, self) | ||
// 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); | ||
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; | ||
self.sync = false | ||
prop.set(this, self) | ||
function onAborted () { | ||
HttpRawStreamOnAborted.call(self); | ||
HttpRawStreamOnAborted.call(that) | ||
} | ||
function onData (chunk) { | ||
HttpRawStreamOnData.call(self, chunk); | ||
HttpRawStreamOnData.call(that, chunk) | ||
} | ||
function onEnd (err) { | ||
HttpRawStreamOnEnd.call(self, err); | ||
HttpRawStreamOnEnd.call(that, err) | ||
} | ||
return this; | ||
}; | ||
return this | ||
} | ||
@@ -251,7 +234,7 @@ /** | ||
*/ | ||
// Avoid using arrow function expression to prevent incorrect "this" | ||
function HttpRawStreamOnAborted () { | ||
var self = this; | ||
const self = prop.get(this) | ||
if (this.complete) return | ||
final.call(this,createError(400, 'request aborted', { | ||
final.call(this, createError(400, 'request aborted', { | ||
code: 'ECONNABORTED', | ||
@@ -262,5 +245,5 @@ expected: self.length, | ||
type: 'request.aborted' | ||
})); | ||
return false; | ||
}; | ||
})) | ||
return false | ||
} | ||
@@ -272,8 +255,9 @@ /** | ||
*/ | ||
function HttpRawStreamOnData( chunk ){ | ||
var self = this; | ||
if (this.complete) return | ||
this.received += chunk.length | ||
if (this.limit !== null && this.received > this.limit) { | ||
// Avoid using arrow function expression to prevent incorrect "this" | ||
function HttpRawStreamOnData (chunk) { | ||
const self = prop.get(this) | ||
if (self.complete) return | ||
self.received += chunk.length | ||
prop.set(this, self) | ||
if (self.limit !== null && self.received > self.limit) { | ||
final.call(this, createError(413, 'request entity too large', { | ||
@@ -284,9 +268,11 @@ limit: self.limit, | ||
})) | ||
} else if (this.decoder) { | ||
this.buffer += this.decoder.write(chunk) | ||
} else if (self.decoder) { | ||
self.buffer += self.decoder.write(chunk) | ||
prop.set(this, self) | ||
} else { | ||
this.buffer.push(chunk) | ||
self.buffer.push(chunk) | ||
prop.set(this, self) | ||
} | ||
return false; | ||
}; | ||
return false | ||
} | ||
@@ -298,8 +284,8 @@ /** | ||
*/ | ||
function HttpRawStreamOnEnd(err){ | ||
var self = this; | ||
if (this.complete) return | ||
// Avoid using arrow function expression to prevent incorrect "this" | ||
function HttpRawStreamOnEnd (err) { | ||
const self = prop.get(this) | ||
if (self.complete) return | ||
if (err) return final.call(this, err) | ||
if (this.length !== null && this.received !== this.length) { | ||
if (self.length !== null && self.received !== self.length) { | ||
final.call(this, createError(400, 'request size did not match content length', { | ||
@@ -312,8 +298,10 @@ expected: self.length, | ||
} else { | ||
var string = this.decoder | ||
? this.buffer + (this.decoder.end() || '') | ||
: Buffer.concat(this.buffer) | ||
final.call(this,null, string) | ||
const string = self.decoder | ||
? self.buffer + (self.decoder.end() || '') | ||
: Buffer.concat(self.buffer) | ||
final.call(this, null, string) | ||
} | ||
return false; | ||
return false | ||
} | ||
module.exports = HttpRawStream |
{ | ||
"name": "xprezzo-raw-body", | ||
"description": "Get and validate the raw body of a readable stream.", | ||
"version": "1.0.6", | ||
"version": "1.0.7", | ||
"author": "Leolio Mcleon <info@leolio.page>", | ||
@@ -6,0 +6,0 @@ "license": "MIT", |
@@ -7,16 +7,3 @@ # xprezzo-raw-body | ||
## Philosophy of Xprezzo | ||
Problems faced: | ||
* Too many requires which creates problem when project grow | ||
* The dependencies update are slow | ||
* Test cases of difficult to design | ||
How Xprezzo try to tackle those problems: | ||
* Useful internal libraries/packages are exposed | ||
* Merge small libraries into a larger one. | ||
* Provide easy to use test framework | ||
## Install | ||
@@ -23,0 +10,0 @@ |
21184
511
173