Skip to content

Commit

Permalink
Do not use it for json() either
Browse files Browse the repository at this point in the history
  • Loading branch information
leo committed Feb 10, 2017
1 parent a4cf235 commit 34eb7cd
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 79 deletions.
77 changes: 34 additions & 43 deletions lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,25 @@ const server = require('http').Server
const getRawBody = require('raw-body')
const typer = require('media-typer')
const isStream = require('isstream')
const Q = require('q')

const DEV = process.env.NODE_ENV === 'development'
const TESTING = process.env.NODE_ENV === 'test'

const serve = fn => server((req, res) => {
run(req, res, fn)
})
const serve = fn => server(Q.async(function * (req, res) {
yield exports.run(req, res, fn)
}))

module.exports = exports = serve

exports.json = json
exports.send = send
exports.sendError = sendError
exports.createError = createError

async function run(req, res, fn) {
exports.run = Q.async(function * (req, res, fn) {
try {
const val = await fn(req, res)
const val = yield fn(req, res)

// Return 204 No Content if value is null
if (val === null) {
send(res, 204, null)
}
Expand All @@ -34,55 +33,47 @@ async function run(req, res, fn) {
send(res, res.statusCode || 200, val)
}
} catch (err) {
await sendError(req, res, err)
}
}

exports.run = (req, res, fn) => {
fn(req, res).then(val => {
// Return 204 No Content if value is null
if (val === null) {
send(res, 204, null)
}

// Return a undefined-null value -> send
if (undefined !== val) {
send(res, res.statusCode || 200, val)
}
}).catch(err => {
sendError(req, res, err)
})
}
}
})

// maps requests to buffered raw bodies so that
// multiple calls to `json` work as expected
const rawBodyMap = new WeakMap()

async function json(req, {limit = '1mb'} = {}) {
const returnJSON = (resolve, reject, str) => {
try {
const type = req.headers['content-type']
const length = req.headers['content-length']
const encoding = typer.parse(type).parameters.charset

let str = rawBodyMap.get(req)
if (!str) {
str = await getRawBody(req, {limit, length, encoding})
rawBodyMap.set(req, str)
}

try {
return JSON.parse(str)
} catch (err) {
throw createError(400, 'Invalid JSON', err)
}
resolve(JSON.parse(str))
} catch (err) {
reject(createError(400, 'Invalid JSON', err))
}
}

exports.json = (req, {limit = '1mb'} = {}) => new Promise((resolve, reject) => {
const type = req.headers['content-type']
const length = req.headers['content-length']
const encoding = typer.parse(type).parameters.charset

let str = rawBodyMap.get(req)

if (str) {
returnJSON(resolve, reject, str)
return
}

getRawBody(req, {limit, length, encoding}).then(buf => {
str = buf
rawBodyMap.set(req, str)

returnJSON(resolve, reject, str)
}).catch(err => {
if (err.type === 'entity.too.large') {
throw createError(413, `Body exceeded ${limit} limit`, err)
} else {
throw createError(400, 'Invalid body', err)
}
}
}
})
})

function send(res, code, obj = null) {
res.statusCode = code
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"isstream": "0.1.2",
"media-typer": "0.3.0",
"node-version": "1.0.0",
"q": "1.4.1",
"raw-body": "2.2.0",
"update-notifier": "1.0.3"
}
Expand Down
56 changes: 20 additions & 36 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -318,30 +318,22 @@ test('json limit (below)', async t => {

test('json limit (over)', async t => {
const fn = async (req, res) => {
const body = await json(req, {
limit: 3
})
let body

try {
body = await json(req, {
limit: 3
})
} catch (err) {
t.deepEqual(err.statusCode, 413)
}

send(res, 200, {
response: body.some.cool
})
}

const url = await getUrl(fn)

try {
await request(url, {
method: 'POST',
body: {
some: {
cool: 'json'
}
},
json: true
})
} catch (err) {
t.deepEqual(err.statusCode, 413)
}
await getUrl(fn)
})

test('json circular', async t => {
Expand Down Expand Up @@ -382,28 +374,20 @@ test('no async', async t => {

test('limit included in error', async t => {
const fn = async (req, res) => {
const body = await json(req, {
limit: 3
})
let body

try {
body = await json(req, {
limit: 3
})
} catch (err) {
t.truthy(/exceeded 3 limit/.test(err.message))
}

send(res, 200, {
response: body.some.cool
})
}

const url = await getUrl(fn)

try {
await request(url, {
method: 'POST',
body: {
some: {
cool: 'json'
}
},
json: true
})
} catch (err) {
t.truthy(/exceeded 3 limit/.test(err.message))
}
await getUrl(fn)
})

0 comments on commit 34eb7cd

Please sign in to comment.