Comparing version 1.3.4 to 2.0.0
@@ -0,1 +1,8 @@ | ||
2.0.0 / 2015-05-08 | ||
================== | ||
* Return a promise without callback instead of thunk | ||
* deps: bytes@2.0.1 | ||
- units no longer case sensitive when parsing | ||
1.3.4 / 2015-04-15 | ||
@@ -2,0 +9,0 @@ ================== |
309
index.js
@@ -0,8 +1,61 @@ | ||
/*! | ||
* raw-body | ||
* Copyright(c) 2013-2014 Jonathan Ong | ||
* Copyright(c) 2014-2015 Douglas Christopher Wilson | ||
* MIT Licensed | ||
*/ | ||
'use strict' | ||
/** | ||
* Module dependencies. | ||
* @private | ||
*/ | ||
var bytes = require('bytes') | ||
var iconv = require('iconv-lite') | ||
module.exports = function (stream, options, done) { | ||
/** | ||
* Module exports. | ||
* @public | ||
*/ | ||
module.exports = getRawBody | ||
/** | ||
* Get the decoder for a given encoding. | ||
* | ||
* @param {string} encoding | ||
* @private | ||
*/ | ||
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 | ||
} | ||
} | ||
/** | ||
* 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 | ||
options = { | ||
opts = { | ||
encoding: options | ||
@@ -12,26 +65,106 @@ } | ||
options = options || {} | ||
if (typeof options === 'function') { | ||
done = options | ||
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 = options.encoding !== true | ||
? options.encoding | ||
var encoding = opts.encoding !== true | ||
? opts.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) | ||
var limit = typeof opts.limit === 'number' ? opts.limit | ||
: typeof opts.limit === 'string' ? bytes(opts.limit) | ||
: null | ||
// convert the expected length to an integer | ||
var length = null | ||
if (options.length != null && !isNaN(options.length)) | ||
length = parseInt(options.length, 10) | ||
var length = opts.length != null && !isNaN(opts.length) | ||
? parseInt(opts.length, 10) | ||
: null | ||
if (done) { | ||
// classic callback style | ||
return readStream(stream, encoding, length, limit, 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() | ||
} | ||
} | ||
/** | ||
* Make a serializable error object. | ||
* | ||
* 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. | ||
* | ||
* @param {string} message | ||
* @param {string} type | ||
* @param {object} props | ||
* @private | ||
*/ | ||
function makeError(message, type, props) { | ||
var error = new Error() | ||
for (var prop in props) { | ||
error[prop] = props[prop] | ||
} | ||
error.message = message | ||
Object.defineProperty(error, 'type', { | ||
value: type, | ||
enumerable: true, | ||
writable: true, | ||
configurable: true | ||
}) | ||
return error | ||
} | ||
/** | ||
* 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) { | ||
// check the length and limit options. | ||
@@ -41,12 +174,13 @@ // note: we intentionally leave the stream paused, | ||
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 () { | ||
var err = makeError('request entity too large', 'entity.too.large', { | ||
expected: length, | ||
length: length, | ||
limit: limit, | ||
status: 413, | ||
statusCode: 413 | ||
}) | ||
return process.nextTick(function () { | ||
done(err) | ||
}) | ||
return defer | ||
} | ||
@@ -62,11 +196,10 @@ | ||
// 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 () { | ||
var err = makeError('stream encoding should not be set', 'stream.encoding.set', { | ||
status: 500, | ||
statusCode: 500 | ||
}) | ||
return process.nextTick(function () { | ||
done(err) | ||
}) | ||
return defer | ||
} | ||
@@ -80,8 +213,5 @@ | ||
} catch (err) { | ||
cleanup() | ||
halt(stream) | ||
process.nextTick(function () { | ||
return process.nextTick(function () { | ||
done(err) | ||
}) | ||
return defer | ||
} | ||
@@ -99,19 +229,22 @@ | ||
return defer | ||
function done(err) { | ||
cleanup() | ||
// yieldable support | ||
function defer(fn) { | ||
done = fn | ||
if (err) { | ||
// halt the stream on error | ||
halt(stream) | ||
} | ||
callback.apply(this, arguments) | ||
} | ||
function onAborted() { | ||
var err = makeError('request aborted', 'request.aborted') | ||
err.code = 'ECONNABORTED' | ||
err.status = 400 | ||
err.received = received | ||
err.length = err.expected = length | ||
cleanup() | ||
halt(stream) | ||
done(err) | ||
done(makeError('request aborted', 'request.aborted', { | ||
code: 'ECONNABORTED', | ||
expected: length, | ||
length: length, | ||
received: received, | ||
status: 400, | ||
statusCode: 400 | ||
})) | ||
} | ||
@@ -126,9 +259,8 @@ | ||
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) | ||
done(makeError('request entity too large', 'entity.too.large', { | ||
limit: limit, | ||
received: received, | ||
status: 413, | ||
statusCode: 413 | ||
})) | ||
} | ||
@@ -138,14 +270,12 @@ } | ||
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) | ||
if (err) return done(err) | ||
if (length !== null && received !== length) { | ||
done(makeError('request size did not match content length', 'request.size.invalid', { | ||
expected: length, | ||
length: length, | ||
received: received, | ||
status: 400, | ||
statusCode: 400 | ||
})) | ||
} else { | ||
@@ -171,52 +301,7 @@ var string = decoder | ||
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 | ||
* @private | ||
*/ | ||
@@ -223,0 +308,0 @@ |
{ | ||
"name": "raw-body", | ||
"description": "Get and validate the raw body of a readable stream.", | ||
"version": "1.3.4", | ||
"version": "2.0.0", | ||
"author": "Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)", | ||
@@ -13,6 +13,7 @@ "contributors": [ | ||
"dependencies": { | ||
"bytes": "1.0.0", | ||
"bytes": "2.0.1", | ||
"iconv-lite": "0.4.8" | ||
}, | ||
"devDependencies": { | ||
"bluebird": "2.9.25", | ||
"istanbul": "0.3.9", | ||
@@ -19,0 +20,0 @@ "mocha": "~2.2.4", |
@@ -16,34 +16,7 @@ # raw-body | ||
var getRawBody = require('raw-body') | ||
var typer = require('media-typer') | ||
app.use(function (req, res, next) { | ||
getRawBody(req, { | ||
length: req.headers['content-length'], | ||
limit: '1mb', | ||
encoding: typer.parse(req.headers['content-type']).parameters.charset | ||
}, function (err, string) { | ||
if (err) | ||
return next(err) | ||
req.text = string | ||
next() | ||
}) | ||
}) | ||
``` | ||
or in a Koa generator: | ||
```js | ||
app.use(function* (next) { | ||
var string = yield getRawBody(this.req, { | ||
length: this.length, | ||
limit: '1mb', | ||
encoding: this.charset | ||
}) | ||
}) | ||
``` | ||
### getRawBody(stream, [options], [callback]) | ||
Returns a thunk for yielding with generators. | ||
**Returns a promise if no callback specified and global `Promise` exists.** | ||
@@ -83,2 +56,59 @@ Options: | ||
## Examples | ||
### Simple Express example | ||
```js | ||
var getRawBody = require('raw-body') | ||
var typer = require('media-typer') | ||
app.use(function (req, res, next) { | ||
getRawBody(req, { | ||
length: req.headers['content-length'], | ||
limit: '1mb', | ||
encoding: typer.parse(req.headers['content-type']).parameters.charset | ||
}, function (err, string) { | ||
if (err) return next(err) | ||
req.text = string | ||
next() | ||
}) | ||
}) | ||
``` | ||
### Simple Koa example | ||
```js | ||
app.use(function* (next) { | ||
var string = yield getRawBody(this.req, { | ||
length: this.length, | ||
limit: '1mb', | ||
encoding: this.charset | ||
}) | ||
}) | ||
``` | ||
### Using as a promise | ||
To use this library as a promise, simply omit the `callback` and a promise is | ||
returned, provided that a global `Promise` is defined. | ||
```js | ||
var getRawBody = require('raw-body') | ||
var http = require('http') | ||
var server = http.createServer(function (req, res) { | ||
getRawBody(req) | ||
.then(function (buf) { | ||
res.statusCode = 200 | ||
res.end(buf.length + ' bytes submitted') | ||
}) | ||
.catch(function (err) { | ||
res.statusCode = 500 | ||
res.end(err.message) | ||
}) | ||
}) | ||
server.listen(3000) | ||
``` | ||
## License | ||
@@ -85,0 +115,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
15321
270
124
5
1
+ Addedbytes@2.0.1(transitive)
- Removedbytes@1.0.0(transitive)
Updatedbytes@2.0.1