forked from hapijs/subtext
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit 5e1db35
authored
Node 4.3.1 or higer is required for proper multipart parse
Until Node commit nodejs/node#4738 Buffer.byteLength function did not work correctly. (This commit was entered in Node 4.3.1 https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V4.md#4.3.1)
On hapijs/pez project, the commit hapijs/pez@9f0042b#diff-6d186b954a58d5bb740f73d84fe39073R162 was depending on Buffer.byteLength to work correctly.
This resulted in a misbehaving multipart parsing when the payload maxBytes is close to the uploaded binary file (Inside the multipart form post payload).
Meaning that Subtext library parsing a multipart payload with a binary file that its content-length is smaller than the maxBytes parameter but is close enough than the inner hapijs/pez libary will count mistakenly for a bigger buffer size and will return an error "Maximum size exceeded" (Subtext will return "Invalid multipart payload format").
I wrote a little program that will help you understand the described scenario.
Notice:
1. You need to put a binary file inside the project for the program to work.
2. In my example the file was 28MB and the limit I entered was 30MB.
Package.json:
{
"name": "subtext_example",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"hapi": "^13.4.1",
"restler": "^3.4.0",
"subtext": "^4.0.4"
}
}
Index.json
'use strict';
const Hapi = require('hapi');
const Subtext = require('subtext');
const Restler = require('restler');
const Fs = require('fs');
const Path = require('path');
const server = new Hapi.Server();
server.connection({ port: 3000 });
var demoFilePath = Path.join(__dirname,"binary_file.exe");
var demoFileMimeType = "application/x-msdownload";
var postUrl = "http://localhost:3000/test_subtext";
var payloadMaxBytes = (30 * 1024 * 1024);
server.route({
method: 'POST',
path: '/test_subtext',
config : {
payload : {
output: 'stream',
parse : false,
timeout : false,
maxBytes : (1024 * 1024 * 1024)
}
},
handler: function (request, reply) {
var subtextParseOptions = {
output: 'file',
parse : true,
timeout : false,
maxBytes : payloadMaxBytes
};
const onParsed = (err, parsed) => {
request.mime = parsed.mime;
request.payload = parsed.payload || null;
if (!err) {
return reply();
}
const failAction = request.route.settings.payload.failAction; // failAction: 'error', 'log', 'ignore'
if (failAction !== 'ignore') {
request._log(['payload', 'error'], err);
}
if (failAction === 'error') {
return reply(err);
}
return reply();
};
request._isPayloadPending = true;
Subtext.parse(request.raw.req, request._tap(), subtextParseOptions, (err, parsed) => {
if (!err ||
!request._isPayloadPending) {
request._isPayloadPending = false;
return onParsed(err, parsed);
}
// Flush out any pending request payload not consumed due to errors
const stream = request.raw.req;
const read = () => {
stream.read();
};
const end = () => {
stream.removeListener('readable', read);
stream.removeListener('error', end);
stream.removeListener('end', end);
request._isPayloadPending = false;
return onParsed(err, parsed);
};
stream.on('readable', read);
stream.once('error', end);
stream.once('end', end);
});
}
});
server.start((err) => {
Fs.stat(demoFilePath, function(err, stats) {
Restler.post(postUrl, {
multipart: true,
data: {
"firstKey": "firstValue",
"filename": Restler.file(demoFilePath, null, stats.size, null, demoFileMimeType)
}
}).on("complete", function(data) {
console.log('Completed: ' + JSON.stringify(data));
});
});
});1 parent 19c43ae commit 5e1db35Copy full SHA for 5e1db35
1 file changed
+1
-1
lines changed+1-1
Original file line number | Diff line number | Diff line change | |
---|---|---|---|
| |||
12 | 12 |
| |
13 | 13 |
| |
14 | 14 |
| |
15 |
| - | |
| 15 | + | |
16 | 16 |
| |
17 | 17 |
| |
18 | 18 |
| |
|
0 commit comments