koa-body-parser
Advanced tools
Comparing version 0.0.1 to 0.1.0
239
index.js
@@ -1,205 +0,80 @@ | ||
var bytes = require('bytes') | ||
var getRawBody = require('raw-body') | ||
var zlib = require('zlib') | ||
exports = module.exports = function (app) { | ||
app.context(context) | ||
module.exports = function (app, options) { | ||
options = options || {} | ||
app.__defineGetter__('bodyLimit', function () { | ||
return this._bodyLimit || 0 | ||
}) | ||
var limit = options.limit || '1mb' | ||
var strict = options.strict !== false | ||
var reviver = options.reviver | ||
var querystring = options.querystring || require('querystring') | ||
app.__defineSetter__('bodyLimit', function (val) { | ||
this._bodyLimit = parseBytes(val) | ||
}) | ||
app.request.urlencoded = function* (lim) { | ||
if (!this.is('application/x-www-form-urlencoded') || this.length === 0) | ||
return | ||
// Default urlencoded parser | ||
app.urlencodedParser = require('querystring').parse | ||
var str = yield* this.string(lim) | ||
return app | ||
} | ||
var context = exports.context = { | ||
// Check whether to allow unstrict JSON | ||
// Does not allow strict JSON by default | ||
get strictJSON() { | ||
return firstBoolean([ | ||
this.app.strictJSON, | ||
this._strictJSON, | ||
true | ||
]) | ||
}, | ||
set strictJSON(val) { | ||
// Should always be boolean | ||
this._strictJSON = !!val | ||
}, | ||
// Check whether the request has a body | ||
get hasBody() { | ||
return !!(this.get('transfer-encoding') || this.length) | ||
}, | ||
// Check the body limit | ||
// "falsey" means no body limit | ||
// contextual body limit takes precedence over app options | ||
get bodyLimit() { | ||
return this._bodyLimit | ||
|| this.app.bodyLimit | ||
|| 0 | ||
}, | ||
// Either a human readable string or a number | ||
// this.bodyLimit = 1024 // 1kb | ||
// this.bodyLimit = '1kb' | ||
set bodyLimit(val) { | ||
this._bodyLimit = parseBytes(val) | ||
try { | ||
return querystring.parse(str, options) | ||
} catch (err) { | ||
err.status = 400 | ||
throw err | ||
} | ||
} | ||
} | ||
/* | ||
Parse the JSON body asynchronously. | ||
Should be used like: | ||
var form = yield this.parseJSON | ||
@api public | ||
*/ | ||
context.parseJSON = function (done) { | ||
if (!this.hasBody || !this.is('json')) | ||
return done() | ||
var limit = this.checkLimit() | ||
var ctx = this | ||
var req = this.req | ||
var strict = this.strictJSON | ||
var received = 0 | ||
var buf = '' | ||
req.setEncoding('utf8') | ||
req.on('data', onData) | ||
req.on('end', onEnd) | ||
function onData(chunk) { | ||
buf += chunk | ||
if (!limit) | ||
app.request.json = function* (lim) { | ||
if (!this.is('json') || this.length === 0) | ||
return | ||
received += chunk.length | ||
if (received <= limit) | ||
return | ||
var str = yield* this.string(lim) | ||
str = str.trim() | ||
// Received > limit, so we destroy the request | ||
cleanup() | ||
ctx.error(createError(413)) | ||
} | ||
if (!str.length) | ||
this.ctx.error(400, 'invalid json, empty body') | ||
function onEnd() { | ||
if (!buf.length) { | ||
cleanup() | ||
return ctx.error(createError(400, 'invalid json, empty body')) | ||
if (strict !== false) { | ||
var first = str[0] | ||
if ('{' !== first && '[' !== first) | ||
this.ctx.error(400, 'invalid json') | ||
} | ||
if (strict) { | ||
var first = buf.trim()[0] | ||
if ('{' !== first && '[' !== first) { | ||
cleanup() | ||
return ctx.error(createError(400, 'invalid json')) | ||
} | ||
try { | ||
return JSON.parse(str, reviver) | ||
} catch (err) { | ||
err.status = 400 | ||
throw err | ||
} | ||
done(null, JSON.parse(buf, ctx.JSONReviver)) | ||
cleanup() | ||
} | ||
function cleanup() { | ||
buf = received = null | ||
req.removeListener('data', onData) | ||
req.removeListener('end', onEnd) | ||
app.request.string = function* (lim) { | ||
var buffer = yield* this.buffer(lim) | ||
return buffer.toString('utf8') | ||
} | ||
} | ||
/* | ||
app.request.buffer = function* (lim) { | ||
var decoder | ||
switch (this.get('content-encoding') || 'identity') { | ||
case 'gzip': | ||
decoder = zlib.createGunzip() | ||
break | ||
case 'deflate': | ||
decoder = zlib.createInflate() | ||
break | ||
case 'identity': | ||
break | ||
default: | ||
this.ctx.error(415, 'invalid content-encoding') | ||
} | ||
Parse the url encoded body asynchronously. | ||
var stream = decoder | ||
? this.req.pipe(decoder) | ||
: this.req | ||
var form = yield this.parseURLEncoded | ||
@api public | ||
*/ | ||
context.parseUrlencoded = | ||
context.parseURLEncoded = function (done) { | ||
if (!this.hasBody || !this.is('application/x-www-form-urlencoded')) | ||
return done() | ||
var limit = this.checkLimit() | ||
var ctx = this | ||
var req = this.req | ||
var received = 0 | ||
var buf = '' | ||
req.setEncoding('utf8') | ||
req.on('data', onData) | ||
req.on('end', onEnd) | ||
function onData(chunk) { | ||
buf += chunk | ||
if (!limit) | ||
return | ||
received += chunk.length | ||
if (received <= limit) | ||
return | ||
// Received > limit, so we destroy the request | ||
cleanup() | ||
ctx.error(createError(413)) | ||
return yield getRawBody(stream, { | ||
limit: lim || limit | ||
}) | ||
} | ||
function onEnd() { | ||
done(null, buf.length ? ctx.app.urlencodedParser(buf) : {}) | ||
cleanup() | ||
} | ||
function cleanup() { | ||
buf = received = null | ||
req.removeListener('data', onData) | ||
req.removeListener('end', onEnd) | ||
} | ||
} | ||
// Check the content-length and return the limit | ||
context.checkLimit = function () { | ||
var limit = this.bodyLimit | ||
var contentLength = this.length | ||
// Could be chunked response | ||
if (contentLength) { | ||
if (limit && contentLength > limit) | ||
throw createError(413) | ||
// We limit the body by the content-length | ||
// in case they lie | ||
limit = contentLength | ||
} | ||
return limit || 0 | ||
} | ||
function parseBytes(val) { | ||
if (typeof val === 'string') | ||
val = bytes(val) | ||
if (!val) | ||
val = 0 | ||
if (typeof val !== 'number') | ||
throw TypeError('this.bodyLimit must either be a string, number, or falsey value') | ||
return val || 0 | ||
} | ||
function firstBoolean(arr) { | ||
for (var i = 0, l = arr.length; i < l; i++) | ||
if (typeof arr[i] === 'boolean') | ||
return arr[i] | ||
} | ||
function createError(status, message) { | ||
var err = new Error(message || '') | ||
err.status = status || 400 | ||
return err | ||
return app | ||
} |
{ | ||
"name": "koa-body-parser", | ||
"description": "Request body parser for koa", | ||
"version": "0.0.1", | ||
"version": "0.1.0", | ||
"author": { | ||
@@ -14,10 +14,10 @@ "name": "Jonathan Ong", | ||
"type": "git", | ||
"url": "https://github.com/jonathanong/koa-body-parser.git" | ||
"url": "https://github.com/koajs/body-parser.git" | ||
}, | ||
"bugs": { | ||
"mail": "me@jongleberry.com", | ||
"url": "https://github.com/jonathanong/koa-body-parser/issues" | ||
"url": "https://github.com/koajs/body-parser/issues" | ||
}, | ||
"dependencies": { | ||
"bytes": "~0.2.1" | ||
"raw-body": "~0.1.1" | ||
}, | ||
@@ -28,6 +28,6 @@ "peerDependencies": { | ||
"devDependencies": { | ||
"koa": "*", | ||
"mocha": "~1.12.0", | ||
"should": "~1.2.2", | ||
"supertest": "~0.7.1" | ||
"koa": "koajs/koa", | ||
"mocha": "*", | ||
"should": "*", | ||
"supertest": "*" | ||
}, | ||
@@ -40,2 +40,2 @@ "scripts": { | ||
} | ||
} | ||
} |
125
README.md
@@ -1,19 +0,14 @@ | ||
# Koa Body Parser [![Build Status](https://travis-ci.org/jonathanong/koa-body-parser.png)](https://travis-ci.org/jonathanong/koa-body-parser) | ||
# Koa Body Parser [![Build Status](https://travis-ci.org/koajs/body-parser.png)](https://travis-ci.org/koajs/body-parser) | ||
Form and JSON body parser for Koa. | ||
This purposely does not support multipart request bodies. | ||
It also supports request body limits. | ||
JSON and urlencoded body parser for Koa. | ||
This does not support multipart request bodies. | ||
It supports request body limits and encoded request bodies. | ||
## Example | ||
## Installation | ||
```js | ||
app.use(function (next) { | ||
return function* () { | ||
var body = (yield this.parseJSON) || (yield this.parseUrlencoded) | ||
// do stuff with your body | ||
} | ||
}) | ||
```bash | ||
$ npm install koa-body-parser | ||
``` | ||
## API | ||
## Example | ||
@@ -25,48 +20,98 @@ ```js | ||
var app = koa() | ||
bodyParser(app) | ||
bodyParser(app, { | ||
limit: '100kb' | ||
}) | ||
// Buffer an image upload | ||
app.use(function* () { | ||
if (this.path !== '/images' || this.method !== 'POST') | ||
return yield next | ||
if (!this.is('image/')) | ||
this.error(415, 'must be an image') | ||
var imageBuffer = yield* this.request.buffer('25mb') | ||
}) | ||
// Parse a form | ||
app.use(function* () { | ||
var body = (yield* this.request.json()) | ||
|| (yield* this.request.form()) | ||
// do stuff with your body | ||
}) | ||
``` | ||
### yield this.parseJSON | ||
## Philosophy | ||
Parses the JSON body. | ||
If you're a masochist, you can also use a callback: | ||
This module aims to create helpful utilities while being as unopinionated as possible. | ||
Thus, these methods may be a little inconvenient, especially when they're only available on `this.request`. | ||
You may want to create your own helper or middleware to parse the body the way you like. | ||
Here is an example: | ||
```js | ||
this.parseJSON(function (err, body) { | ||
function* bodyParser() { | ||
if (this.req.checkContinue) | ||
this.req.writeContinue() | ||
var body = (yield* this.request.urlencoded()) | ||
|| (yield* this.request.json()) | ||
if (!body) | ||
this.error(400, 'no body!?') | ||
return body | ||
} | ||
app.use(function* (next) { | ||
if (this.path !== '/posts' || this.method !== 'POST') | ||
return yield next | ||
var body = yield* bodyParser() | ||
}) | ||
``` | ||
### yield this.parseUrlencoded | ||
Of course, there are many ways to do this! | ||
Parses the urlencoded body. | ||
Same signature as `this.parseJSON` | ||
## API | ||
### app.strictJSON, this.strictJSON | ||
### bodyParser(app, [options]) | ||
By default, JSON must be strict. | ||
You may override the default behavior by doing `app.strictJSON = false` or `this.strictJSON = false`. | ||
`this.strictJSON` takes precedent over `app.strictJSON`. | ||
Buffer options: | ||
### app.bodyLimit, this.bodyLimit | ||
- `limit` [1mb] - request body byte limit. | ||
Sets the request body limit. | ||
By default, there is no limit. | ||
You can either set it app wide, or context wide. | ||
`this.bodyLimit` takes precence over `app.bodyLimit`. | ||
JSON parsing options: | ||
You can set it using numerical bytes like `app.bodyLimit = 5 * 1024 * 1024`, or you can set it in human readable form like `app.bodyLimit = '5mb'`. | ||
- `strict` [true] - if `false`, anything `JSON.parse()` accepts will be parsed as JSON, | ||
otherwise only objects and arrays will be accepted. | ||
- `reviver` - used as the second `reviver` argument for `JSON.parse()`. | ||
For example, you can set a app-wide limit of `100kb` by setting `app.bodyLimit = '1mb'`. | ||
Then, perhaps for a route in which users upload an image, you can manually set the limit to `this.bodyLimit = '10mb'`. | ||
Urlencoded options: | ||
### app.urlencodedParser | ||
- `querystring` [require('querystring')] - the querystring parser. Node's by default. If you want to use `qs`, set it as `require('qs')`. | ||
- `maxKeys` [1000] - maximum number of keys. Passed to [querystring.parse](http://nodejs.org/api/querystring.html#querystring_querystring_parse_str_sep_eq_options) | ||
By default, url encoded bodies are parsed using the native `querystring` module. | ||
If you want to use your own parser, for example the `qs` library, you can overwrite this method. | ||
### yield* this.request.json([limit]) | ||
```js | ||
app.urlencodedParser = require('qs').parse | ||
``` | ||
Parses the JSON body. | ||
The function call is required. | ||
`limit` is the optional limit in bytes integer for the body that overrides the default. | ||
### yield* this.request.urlencoded([limit]) | ||
Parses the urlencoded body. | ||
The function call is required. | ||
`limit` is the optional limit in bytes integer for the body that overrides the default. | ||
### yield* this.request.string([limit]) | ||
Buffers the response and returns a utf8 string. | ||
The function call is required. | ||
`limit` is the optional limit in bytes integer for the body that overrides the default. | ||
### yields* this.request.buffer([limit]) | ||
Buffers the response and returns a raw `Buffer` instance. | ||
The function call is required. | ||
`limit` is the optional limit in bytes integer for the body that overrides the default. | ||
## License | ||
@@ -94,2 +139,2 @@ | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. | ||
THE SOFTWARE. |
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
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
1
139
6986
64
1
+ Addedraw-body@~0.1.1
+ Addedraw-body@0.1.1(transitive)
- Removedbytes@~0.2.1