-
Notifications
You must be signed in to change notification settings - Fork 24
/
index.js
156 lines (132 loc) · 3.99 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
145
146
147
148
149
150
151
152
153
154
155
156
var Busboy = require('busboy')
var chan = require('chan')
var BlackHoleStream = require('black-hole-stream')
var inflate = require('inflation')
var getDescriptor = Object.getOwnPropertyDescriptor
var isArray = Array.isArray
module.exports = function (request, options) {
var ch = chan()
var parts = function (fn) {
if (fn) return ch(fn)
return new Promise(function (resolve, reject) {
ch(function (err, res) {
if (err) return reject(err)
resolve(res)
})
})
}
// 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)`
var checkField = options.checkField
var checkFile = options.checkFile
var lastError
var busboy = Busboy(options)
request = inflate(request)
request.on('close', cleanup)
busboy
.on('field', onField)
.on('file', onFile)
.on('close', cleanup)
.on('error', onEnd)
.on('finish', onEnd)
busboy.on('partsLimit', function () {
var err = new Error('Reach parts limit')
err.code = 'Request_parts_limit'
err.status = 413
onError(err)
})
busboy.on('filesLimit', function () {
var err = new Error('Reach files limit')
err.code = 'Request_files_limit'
err.status = 413
onError(err)
})
busboy.on('fieldsLimit', function () {
var err = new Error('Reach fields limit')
err.code = 'Request_fields_limit'
err.status = 413
onError(err)
})
request.pipe(busboy)
// i would just put everything in an array
// but people will complain
if (options.autoFields) {
var field = parts.field = {} // object lookup
var fields = parts.fields = [] // list lookup
}
return parts
function onField(name, val, info) {
var fieldnameTruncated = info.nameTruncated
var valTruncated = info.valueTruncated
if (checkField) {
var err = checkField(name, val, fieldnameTruncated, valTruncated)
if (err) {
return onError(err)
}
}
var args = [ name, val, fieldnameTruncated, valTruncated ]
if (options.autoFields) {
fields.push(args)
// don't overwrite prototypes
if (getDescriptor(Object.prototype, name)) return
var prev = field[ name ]
if (prev == null) return field[ name ] = val
if (isArray(prev)) return prev.push(val)
field[ name ] = [ prev, val ]
} else {
ch(args)
}
}
function onFile(fieldname, file, info) {
function onFileError(err) {
lastError = err
}
function onFileCleanup() {
file.removeListener('error', onFileError)
file.removeListener('end', onFileCleanup)
file.removeListener('close', onFileCleanup)
}
file.on('error', onFileError)
file.on('end', onFileCleanup)
file.on('close', onFileCleanup)
var filename = info.filename
var encoding = info.encoding
var mimetype = info.mimeType
if (checkFile) {
var err = checkFile(fieldname, file, filename, encoding, mimetype)
if (err) {
// make sure request stream's data has been read
var blackHoleStream = new BlackHoleStream()
file.pipe(blackHoleStream)
return onError(err)
}
}
// opinionated, but 5 arguments is ridiculous
file.fieldname = fieldname
file.filename = filename
file.transferEncoding = file.encoding = encoding
file.mimeType = file.mime = mimetype
ch(file)
}
function onError(err) {
lastError = err
}
function onEnd() {
cleanup()
busboy.removeListener('finish', onEnd)
busboy.removeListener('error', onEnd)
ch(lastError)
}
function cleanup() {
// keep finish listener to wait all data flushed
// keep error listener to wait stream error
request.removeListener('close', cleanup)
busboy.removeListener('field', onField)
busboy.removeListener('file', onFile)
busboy.removeListener('close', cleanup)
}
}