diff --git a/lib/incoming_form.js b/lib/incoming_form.js index fc554b71..dbf84793 100644 --- a/lib/incoming_form.js +++ b/lib/incoming_form.js @@ -344,98 +344,103 @@ IncomingForm.prototype._initMultipart = function(boundary) { parser.initWithBoundary(boundary); - parser.onPartBegin = function() { - part = new Stream(); - part.readable = true; - part.headers = {}; - part.name = null; - part.filename = null; - part.mime = null; - - part.transferEncoding = 'binary'; - part.transferBuffer = ''; - - headerField = ''; - headerValue = ''; - }; - - parser.onHeaderField = (b, start, end) => { - headerField += b.toString(this.encoding, start, end); - }; - - parser.onHeaderValue = (b, start, end) => { - headerValue += b.toString(this.encoding, start, end); - }; - - parser.onHeaderEnd = () => { - headerField = headerField.toLowerCase(); - part.headers[headerField] = headerValue; - - // matches either a quoted-string or a token (RFC 2616 section 19.5.1) - var m = headerValue.match(/\bname=("([^"]*)"|([^\(\)<>@,;:\\"\/\[\]\?=\{\}\s\t/]+))/i); - if (headerField == 'content-disposition') { - if (m) { - part.name = m[2] || m[3] || ''; - } + parser.on('data', ({name, buffer, start, end}) => { + if (name === 'partBegin') { + part = new Stream(); + part.readable = true; + part.headers = {}; + part.name = null; + part.filename = null; + part.mime = null; + + part.transferEncoding = 'binary'; + part.transferBuffer = ''; + + headerField = ''; + headerValue = ''; + } else if (name === 'headerField') { + headerField += buffer.toString(this.encoding, start, end); + } else if (name === 'headerValue') { + headerValue += buffer.toString(this.encoding, start, end); + } else if (name === 'headerEnd') { + headerField = headerField.toLowerCase(); + part.headers[headerField] = headerValue; + + // matches either a quoted-string or a token (RFC 2616 section 19.5.1) + var m = headerValue.match(/\bname=("([^"]*)"|([^\(\)<>@,;:\\"\/\[\]\?=\{\}\s\t/]+))/i); + if (headerField == 'content-disposition') { + if (m) { + part.name = m[2] || m[3] || ''; + } - part.filename = this._fileName(headerValue); - } else if (headerField == 'content-type') { - part.mime = headerValue; - } else if (headerField == 'content-transfer-encoding') { - part.transferEncoding = headerValue.toLowerCase(); - } + part.filename = this._fileName(headerValue); + } else if (headerField == 'content-type') { + part.mime = headerValue; + } else if (headerField == 'content-transfer-encoding') { + part.transferEncoding = headerValue.toLowerCase(); + } - headerField = ''; - headerValue = ''; - }; + headerField = ''; + headerValue = ''; + } else if (name === 'headersEnd') { + + switch(part.transferEncoding){ + case 'binary': + case '7bit': + case '8bit': { + const dataPropagation = ({name, buffer, start, end}) => { + if (name === 'partData') { + part.emit('data', buffer.slice(start, end)); + } + }; + const dataStopPropagation = ({name}) => { + if (name === 'partEnd') { + part.emit('end'); + parser.off('data', dataPropagation); + parser.off('data', dataStopPropagation); + } + }; + parser.on('data', dataPropagation); + parser.on('data', dataStopPropagation); + break; + } case 'base64': { + const dataPropagation = ({name, buffer, start, end}) => { + if (name === 'partData') { + part.transferBuffer += buffer.slice(start, end).toString('ascii'); + + /* + four bytes (chars) in base64 converts to three bytes in binary + encoding. So we should always work with a number of bytes that + can be divided by 4, it will result in a number of buytes that + can be divided vy 3. + */ + var offset = parseInt(part.transferBuffer.length / 4, 10) * 4; + part.emit('data', Buffer.from(part.transferBuffer.substring(0, offset), 'base64')); + part.transferBuffer = part.transferBuffer.substring(offset); + } + }; + const dataStopPropagation = ({name}) => { + if (name === 'partEnd') { + part.emit('data', Buffer.from(part.transferBuffer, 'base64')); + part.emit('end'); + parser.off('data', dataPropagation); + parser.off('data', dataStopPropagation); + } + }; + parser.on('data', dataPropagation); + parser.on('data', dataStopPropagation); + break; + + } default: + return this._error(new Error('unknown transfer-encoding')); + } - parser.onHeadersEnd = () => { - switch(part.transferEncoding){ - case 'binary': - case '7bit': - case '8bit': - parser.onPartData = function(b, start, end) { - part.emit('data', b.slice(start, end)); - }; - - parser.onPartEnd = function() { - part.emit('end'); - }; - break; - - case 'base64': - parser.onPartData = function(b, start, end) { - part.transferBuffer += b.slice(start, end).toString('ascii'); - - /* - four bytes (chars) in base64 converts to three bytes in binary - encoding. So we should always work with a number of bytes that - can be divided by 4, it will result in a number of buytes that - can be divided vy 3. - */ - var offset = parseInt(part.transferBuffer.length / 4, 10) * 4; - part.emit('data', Buffer.from(part.transferBuffer.substring(0, offset), 'base64')); - part.transferBuffer = part.transferBuffer.substring(offset); - }; - - parser.onPartEnd = function() { - part.emit('data', Buffer.from(part.transferBuffer, 'base64')); - part.emit('end'); - }; - break; - - default: - return this._error(new Error('unknown transfer-encoding')); + this.onPart(part); + } else if (name === 'end') { + this.ended = true; + this._maybeEnd(); } - - this.onPart(part); - }; - - - parser.onEnd = () => { - this.ended = true; - this._maybeEnd(); - }; + }); this._parser = parser; };