forked from cojs/busboy
-
Notifications
You must be signed in to change notification settings - Fork 5
/
index.js
144 lines (116 loc) · 3.64 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
135
136
137
138
139
140
141
142
143
144
'use strict'
const Busboy = require('busboy')
const BlackHoleStream = require('black-hole-stream')
const Result = require('./result')
const getDescriptor = Object.getOwnPropertyDescriptor
const isArray = Array.isArray
module.exports = (request, options) => {
const res = new Result()
// - what used to be a `chan` will now be a thenable
// - each time the thenable.then() is called, a Promise is returned. when the
// next value is ready, the promise is resolved with it OR if the value is an
// error, the promise is rejected and the thenable is marked done.
// - if a done thenable is awaited, a Promise resolved with
// an empty value is returned.
// koa special sauce
request = request.req || request
options = options || {}
options.headers = request.headers
// options.checkField hook `function(name, val, fieldnameTruncated, valTruncated)`
// options.checkFile hook `function(fieldname, fileStream, filename, encoding, mimetype)`
const checkField = options.checkField
const checkFile = options.checkFile
let lastError
const busboy = new Busboy(options)
request.on('close', cleanup)
busboy
.on('field', onField)
.on('file', onFile)
.on('close', cleanup)
.on('error', onEnd)
.on('finish', onEnd)
busboy.on('partsLimit', () => {
const err = new Error('Reach parts limit')
err.code = 'Request_parts_limit'
err.status = 413
onError(err)
})
busboy.on('filesLimit', () => {
const err = new Error('Reach files limit')
err.code = 'Request_files_limit'
err.status = 413
onError(err)
})
busboy.on('fieldsLimit', () => {
const err = new Error('Reach fields limit')
err.code = 'Request_fields_limit'
err.status = 413
onError(err)
})
request.pipe(busboy)
if (options.autoFields) {
var field = res.field = {} // object lookup
var fields = res.fields = [] // list lookup
}
return res
function onField (name, val, fieldnameTruncated, valTruncated) {
if (checkField) {
const err = checkField(name, val, fieldnameTruncated, valTruncated)
if (err) {
return onError(err)
}
}
const args = [name, val, fieldnameTruncated, valTruncated]
if (options.autoFields) {
fields.push(args)
// don't overwrite prototypes
if (getDescriptor(Object.prototype, name)) return
const prev = field[name]
if (prev == null) {
field[name] = val
return
}
if (isArray(prev)) {
prev.push(val)
return
}
field[name] = [prev, val]
} else {
res.add(args)
}
}
function onFile (fieldname, file, filename, encoding, mimetype) {
if (checkFile) {
const err = checkFile(fieldname, file, filename, encoding, mimetype)
if (err) {
// make sure request stream's data has been read
const blackHoleStream = new BlackHoleStream()
file.pipe(blackHoleStream)
return onError(err)
}
}
file.fieldname = fieldname
file.filename = filename
file.transferEncoding = file.encoding = encoding
file.mimeType = file.mime = mimetype
res.add(file)
}
function onError (err) {
lastError = err
}
function onEnd (err) {
cleanup()
res.close(err || lastError)
}
function cleanup () {
request.removeListener('close', cleanup)
busboy.removeListener('field', onField)
busboy.removeListener('file', onFile)
busboy.removeListener('close', cleanup)
busboy.removeListener('error', onEnd)
busboy.removeListener('partsLimit', onEnd)
busboy.removeListener('filesLimit', onEnd)
busboy.removeListener('fieldsLimit', onEnd)
busboy.removeListener('finish', onEnd)
}
}