|
|
@ -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) |
|
|
|
} |
|
|
@ -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 |
|
|
|