index.js
3.3 KB
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
var Busboy = require('busboy')
var chan = require('chan')
var BlackHoleStream = require('black-hole-stream')
var getDescriptor = Object.getOwnPropertyDescriptor
var isArray = Array.isArray;
module.exports = function (request, options) {
var ch = chan()
// 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 = 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', 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 = ch.field = {} // object lookup
var fields = ch.fields = [] // list lookup
}
return ch
function onField(name, val, fieldnameTruncated, valTruncated) {
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, filename, encoding, 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()
ch(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)
}
}