Skip to content

Commit

Permalink
Return a promise without callback instead of thunk
Browse files Browse the repository at this point in the history
closes #26
  • Loading branch information
dougwilson committed May 8, 2015
1 parent 49ff30d commit 0b618d5
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 58 deletions.
5 changes: 5 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
2.x
===

* Return a promise without callback instead of thunk

1.3.4 / 2015-04-15
==================

Expand Down
86 changes: 58 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,11 @@ Ideal for parsing request bodies.

```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()
})
})
```

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.**

Options:

Expand Down Expand Up @@ -79,6 +52,63 @@ and you are responsible for correctly disposing the stream.
For HTTP requests, no handling is required if you send a response.
For streams that use file descriptors, you should `stream.destroy()` or `stream.close()` to prevent leaks.

## 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

[MIT](LICENSE)
Expand Down
29 changes: 20 additions & 9 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ function getRawBody(stream, options, callback) {
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 = opts.encoding !== true
? opts.encoding
Expand All @@ -82,16 +92,17 @@ function getRawBody(stream, options, callback) {
? parseInt(opts.length, 10)
: null

readStream(stream, encoding, length, limit, function () {
done.apply(this, arguments)
})

return defer

// yieldable support
function defer(callback) {
done = callback
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)
})
})
}

/**
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"iconv-lite": "0.4.8"
},
"devDependencies": {
"bluebird": "2.9.25",
"istanbul": "0.3.9",
"mocha": "~2.2.4",
"readable-stream": "~1.0.33",
Expand Down
95 changes: 74 additions & 21 deletions test/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
var assert = require('assert')
var fs = require('fs')
var path = require('path')
var getRawBody = require('..')
var http = require('http')
var net = require('net')
var path = require('path')
var through = require('through2')
var Readable = require('readable-stream').Readable

var getRawBody = require('../')
var Promise = global.Promise || require('bluebird')
var Readable = require('readable-stream').Readable

var file = path.join(__dirname, 'index.js')
var length = fs.statSync(file).size
Expand All @@ -29,13 +30,10 @@ describe('Raw Body', function () {
})
})

it('should work as a thunk', function (done) {
var thunk = getRawBody(createStream())
thunk(function (err, buf) {
assert.ifError(err)
checkBuffer(buf)
done()
})
it('should error for bad callback', function () {
assert.throws(function () {
getRawBody(createStream(), true, 'silly')
}, /argument callback.*function/)
})

it('should work with length', function (done) {
Expand Down Expand Up @@ -118,17 +116,6 @@ describe('Raw Body', function () {
})
})

it('should work as a thunk when length > limit', function (done) {
var thunk = getRawBody(createStream(), {
length: length,
limit: length - 1
})
thunk(function (err, buf) {
assert.equal(err.status, 413)
done()
})
})

it('should work with an empty stream', function (done) {
var stream = new Readable()
stream.push(null)
Expand Down Expand Up @@ -229,6 +216,63 @@ describe('Raw Body', function () {
})
})

describe('with global Promise', function () {
before(function () {
global.Promise = Promise
})

after(function () {
global.Promise = undefined
})

it('should work as a promise', function () {
return getRawBody(createStream())
.then(checkBuffer)
})

it('should work as a promise when length > limit', function () {
return getRawBody(createStream(), {
length: length,
limit: length - 1
})
.then(throwExpectedError, function (err) {
assert.equal(err.status, 413)
})
})
})

describe('without global Promise', function () {
before(function () {
global.Promise = undefined
})

after(function () {
global.Promise = Promise
})

it('should error without callback', function () {
assert.throws(function () {
getRawBody(createStream())
}, /argument callback.*required/)
})

it('should work with callback as second argument', function (done) {
getRawBody(createStream(), function (err, buf) {
assert.ifError(err)
checkBuffer(buf)
done()
})
})

it('should work with callback as third argument', function (done) {
getRawBody(createStream(), true, function (err, str) {
assert.ifError(err)
checkString(str)
done()
})
})
})

describe('when an encoding is set', function () {
it('should return a string', function (done) {
getRawBody(createStream(), {
Expand Down Expand Up @@ -446,6 +490,11 @@ function checkBuffer(buf) {
assert.equal(buf.toString('utf8'), string)
}

function checkString(str) {
assert.ok(typeof str === 'string')
assert.equal(str, string)
}

function createStream(buf) {
if (!buf) return fs.createReadStream(file)

Expand All @@ -457,3 +506,7 @@ function createStream(buf) {

return stream
}

function throwExpectedError() {
throw new Error('expected error')
}

0 comments on commit 0b618d5

Please sign in to comment.