1 | 'use strict'
|
2 |
|
3 | const Busboy = require('busboy')
|
4 | const BlackHoleStream = require('black-hole-stream')
|
5 | const Result = require('./result')
|
6 |
|
7 | const getDescriptor = Object.getOwnPropertyDescriptor
|
8 | const isArray = Array.isArray
|
9 |
|
10 | module.exports = (request, options) => {
|
11 | const res = new Result()
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 | request = request.req || request
|
22 |
|
23 | options = options || {}
|
24 | options.headers = request.headers
|
25 |
|
26 |
|
27 | const checkField = options.checkField
|
28 | const checkFile = options.checkFile
|
29 | let lastError
|
30 |
|
31 | const busboy = new Busboy(options)
|
32 |
|
33 | request.on('close', cleanup)
|
34 |
|
35 | busboy
|
36 | .on('field', onField)
|
37 | .on('file', onFile)
|
38 | .on('close', cleanup)
|
39 | .on('error', onEnd)
|
40 | .on('finish', onEnd)
|
41 |
|
42 | busboy.on('partsLimit', () => {
|
43 | const err = new Error('Reach parts limit')
|
44 | err.code = 'Request_parts_limit'
|
45 | err.status = 413
|
46 | onError(err)
|
47 | })
|
48 |
|
49 | busboy.on('filesLimit', () => {
|
50 | const err = new Error('Reach files limit')
|
51 | err.code = 'Request_files_limit'
|
52 | err.status = 413
|
53 | onError(err)
|
54 | })
|
55 |
|
56 | busboy.on('fieldsLimit', () => {
|
57 | const err = new Error('Reach fields limit')
|
58 | err.code = 'Request_fields_limit'
|
59 | err.status = 413
|
60 | onError(err)
|
61 | })
|
62 |
|
63 | request.pipe(busboy)
|
64 |
|
65 | if (options.autoFields) {
|
66 | var field = res.field = {}
|
67 | var fields = res.fields = []
|
68 | }
|
69 |
|
70 | return res
|
71 |
|
72 | function onField (name, val, fieldnameTruncated, valTruncated) {
|
73 | if (checkField) {
|
74 | const err = checkField(name, val, fieldnameTruncated, valTruncated)
|
75 | if (err) {
|
76 | return onError(err)
|
77 | }
|
78 | }
|
79 |
|
80 | const args = [name, val, fieldnameTruncated, valTruncated]
|
81 |
|
82 | if (options.autoFields) {
|
83 | fields.push(args)
|
84 |
|
85 |
|
86 | if (getDescriptor(Object.prototype, name)) return
|
87 |
|
88 | const prev = field[name]
|
89 |
|
90 | if (prev == null) {
|
91 | field[name] = val
|
92 | return
|
93 | }
|
94 |
|
95 | if (isArray(prev)) {
|
96 | prev.push(val)
|
97 | return
|
98 | }
|
99 |
|
100 | field[name] = [prev, val]
|
101 | } else {
|
102 | res.add(args)
|
103 | }
|
104 | }
|
105 |
|
106 | function onFile (fieldname, file, filename, encoding, mimetype) {
|
107 | if (checkFile) {
|
108 | const err = checkFile(fieldname, file, filename, encoding, mimetype)
|
109 | if (err) {
|
110 |
|
111 | const blackHoleStream = new BlackHoleStream()
|
112 | file.pipe(blackHoleStream)
|
113 | return onError(err)
|
114 | }
|
115 | }
|
116 |
|
117 | file.fieldname = fieldname
|
118 | file.filename = filename
|
119 | file.transferEncoding = file.encoding = encoding
|
120 | file.mimeType = file.mime = mimetype
|
121 | res.add(file)
|
122 | }
|
123 |
|
124 | function onError (err) {
|
125 | lastError = err
|
126 | }
|
127 |
|
128 | function onEnd (err) {
|
129 | cleanup()
|
130 | res.close(err || lastError)
|
131 | }
|
132 |
|
133 | function cleanup () {
|
134 | request.removeListener('close', cleanup)
|
135 | busboy.removeListener('field', onField)
|
136 | busboy.removeListener('file', onFile)
|
137 | busboy.removeListener('close', cleanup)
|
138 | busboy.removeListener('error', onEnd)
|
139 | busboy.removeListener('partsLimit', onEnd)
|
140 | busboy.removeListener('filesLimit', onEnd)
|
141 | busboy.removeListener('fieldsLimit', onEnd)
|
142 | busboy.removeListener('finish', onEnd)
|
143 | }
|
144 | }
|