'use strict'; /** * Module dependencies. */ const raw = require('raw-body'); const inflate = require('inflation'); const { safeParse } = require('@poppinss/utils'); const utils = require('./utils'); // Allowed whitespace is defined in RFC 7159 // http://www.rfc-editor.org/rfc/rfc7159.txt const strictJSONReg = /^[\x20\x09\x0a\x0d]*(\[|\{)/; function convertEmptyStringsToNull(key, value) { if (key === '') { return value; } if (value === '') { return null; } return value; } /** * Return a Promise which parses json requests. * * Pass a node request or an object with `.req`, * such as a koa Context. * * @param {Request} req * @param {Options} [opts] * @return {Function} * @api public */ module.exports = async function(req, opts) { req = req.req || req; opts = utils.clone(opts); // defaults let len = req.headers['content-length']; const encoding = req.headers['content-encoding'] || 'identity'; if (len && encoding === 'identity') opts.length = len = ~~len; opts.encoding = opts.encoding || 'utf8'; opts.limit = opts.limit || '1mb'; const strict = opts.strict !== false; const reviver = opts.convertEmptyStringsToNull ? convertEmptyStringsToNull : undefined; const str = await raw(inflate(req), opts); try { const parsed = parse(str); return opts.returnRawBody ? { parsed, raw: str } : parsed; } catch (err) { err.status = 400; err.body = str; throw err; } function parse(str) { if (!strict) { return str ? safeParse(str, reviver) : str; } // strict mode always return object if (!str) return {}; // strict JSON test if (!strictJSONReg.test(str)) { throw new Error('invalid JSON, only supports object and array'); } return safeParse(str, reviver); } };