This repository has been archived by the owner on Aug 17, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
134 lines (117 loc) · 3.52 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
'use strict'
// **Github:** https://github.com/toajs/toa-body
//
// **License:** MIT
/**
* modified from https://github.com/koajs/body-parser
*/
const qs = require('qs')
const raw = require('raw-body')
const inflate = require('inflation')
const thunk = require('thunks').thunk
// Allowed whitespace is defined in RFC 7159
// http://www.rfc-editor.org/rfc/rfc7159.txt
const strictJSONReg = /^[\x20\x09\x0a\x0d]*(\[|\{)/ // eslint-disable-line
/**
* @param [Object] opts
* - {String} jsonLimit default '1mb'
* - {String} formLimit default '56kb'
* - {string} encoding default 'utf8'
* - {Object} extendTypes
*/
module.exports = function toaBody (app, opts) {
if (app && !app.context) {
opts = app
app = null
}
opts = opts || {}
const extendTypes = opts.extendTypes || {}
// default json types
const jsonTypes = [
'application/json',
'application/json-patch+json',
'application/vnd.api+json',
'application/csp-report'
]
// default form types
const formTypes = [
'application/x-www-form-urlencoded'
]
extendType(jsonTypes, extendTypes.json)
extendType(formTypes, extendTypes.form)
const jsonOpts = getOptions({jsonLimit: '1mb', encoding: 'utf8'}, opts, 'json')
const formOpts = getOptions({jsonLimit: '56kb', encoding: 'utf8'}, opts, 'form')
const defaultOpts = getOptions({defaultLimit: '1mb'}, opts, 'default')
const jsonParse = getJsonParse(opts.strict !== false)
const formParse = getFormParse(opts.qs || qs, opts.qsOptions)
const defaultParse = opts.parse || ((value) => (value instanceof Buffer && !value.length) ? null : value)
function parseBody () {
let options = defaultOpts
let parse = defaultParse
let body = this.request.body
if (body !== undefined) return Promise.resolve(body)
if (this.is(jsonTypes)) {
parse = jsonParse
options = jsonOpts
} else if (this.is(formTypes)) {
parse = formParse
options = formOpts
}
return getRawBody(this.req, options)
.then((str) => {
return new Promise((resolve, reject) => {
thunk(parse.call(this, str))((err, res) => {
if (err != null) reject(err)
else resolve(res)
})
}).catch((err) => {
err.status = 400
err.data = str
throw err
})
})
.then((res) => {
this.request.body = res
return res
})
}
if (app) {
if (app.context.parseBody) throw new Error('app.context.parseBody is exist!')
app.request.body = undefined
app.context.parseBody = parseBody
}
return parseBody
}
function getOptions (opts1, opts2, type) {
let res = Object.assign({}, opts1, opts2)
let limit = res[type + 'Limit']
if (limit) res.limit = limit
return res
}
function extendType (original, extend) {
if (extend) {
if (!Array.isArray(extend)) extend = [extend]
original.push.apply(original, extend)
}
}
function getRawBody (req, opts) {
let len = req.headers['content-length']
let encoding = req.headers['content-encoding'] || 'identity'
if (len && encoding === 'identity') opts.length = ~~len
return raw(inflate(req), opts)
}
function getJsonParse (strict) {
return (str) => {
if (!strict) return str ? JSON.parse(str) : 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 JSON.parse(str)
}
}
function getFormParse (qs, qsOptions) {
return (str) => qs.parse(str, qsOptions)
}