Skip to content

Commit

Permalink
use the new multipartparser which is a stream
Browse files Browse the repository at this point in the history
  • Loading branch information
GrosSacASac authored Dec 17, 2019
1 parent 214708a commit b688b66
Showing 1 changed file with 93 additions and 88 deletions.
181 changes: 93 additions & 88 deletions lib/incoming_form.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down

0 comments on commit b688b66

Please sign in to comment.