Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions src/plugins/octetstream.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable no-underscore-dangle */

import OctetStreamParser from "../parsers/OctetStream.js";
import FormidableError, * as errors from "../FormidableError.js";

export const octetStreamType = "octet-stream";
// the `options` is also available through the `options` / `formidable.options`
Expand Down Expand Up @@ -29,6 +30,11 @@ async function init(_self, _opts) {
originalFilename,
mimetype,
};

if (!this.options.filter({ name: "file", ...thisPart })) {
return ignoreFile.call(this);
}

const newFilename = this._getNewName(thisPart);
const filepath = this._joinDirectoryName(newFilename);
const file = await this._newFile({
Expand Down Expand Up @@ -63,6 +69,28 @@ async function init(_self, _opts) {
});

this._parser.on("end", () => {
const bodySize = this.bytesReceived;
if (!this.options.allowEmptyFiles && bodySize === 0) {
this._error(
new FormidableError(
`options.allowEmptyFiles is false, file size should be greater than 0`,
errors.noEmptyFiles,
400
)
);
return;
}
if (bodySize < this.options.minFileSize) {
this._error(
new FormidableError(
`options.minFileSize (${this.options.minFileSize} bytes) inferior, received ${bodySize} bytes of file data`,
errors.smallerThanMinFileSize,
400
)
);
return;
}

this._flushing -= 1;
this.ended = true;

Expand All @@ -82,3 +110,13 @@ async function init(_self, _opts) {

return this;
}

function ignoreFile() {
this._parser = new OctetStreamParser(this.options);
this._parser.on("end", () => {
this.ended = true;
this._maybeEnd();
});
this._parser.resume();
return this;
}
133 changes: 133 additions & 0 deletions test/integration/octet-stream-file-checks.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { unlinkSync } from "node:fs";
import { createServer, request as _request } from "node:http";
import assert, { strictEqual, deepStrictEqual } from "node:assert";

import formidable from "../../src/index.js";
import {
noEmptyFiles,
smallerThanMinFileSize,
} from "../../src/FormidableError.js";

function upload({ port, options, headers, body }, check, done) {
let request;
let parsed = false;
const server = createServer((req, res) => {
const form = formidable(options);

form.parse(req, (err, fields, files) => {
parsed = true;
res.end();
request.destroy();
server.close();
try {
check(err, files);
done();
} catch (error) {
done(error);
} finally {
Object.values(files)
.flat()
.forEach((file) => unlinkSync(file.filepath));
}
});
});

server.listen(port, (err) => {
assert(!err, "should not have error, but be falsey");

request = _request({
port,
method: "POST",
headers: { "Content-Type": "application/octet-stream", ...headers },
});

request.on("error", (error) => {
if (parsed) return;
server.close();
done(error);
});
request.end(body);
});
}

test("octet stream applies filter", (done) => {
const calls = [];
upload(
{
port: 13552,
options: {
filter: ({ name, originalFilename, mimetype }) => {
calls.push({ name, originalFilename, mimetype });
return false;
},
},
headers: { "X-File-Name": "a.bin" },
body: Buffer.alloc(1000, 0x42),
},
(err, files) => {
assert(!err, "a filtered file should not make the parse fail");
strictEqual(Object.keys(files).length, 0);
deepStrictEqual(calls, [
{
name: "file",
originalFilename: "a.bin",
mimetype: "application/octet-stream",
},
]);
},
done
);
});

test("octet stream enforces minFileSize", (done) => {
upload(
{
port: 13553,
options: { minFileSize: 1000 },
body: Buffer.alloc(10, 0x42),
},
(err, files) => {
assert(
err,
"expected an error for an octet-stream upload under minFileSize"
);
strictEqual(err.code, smallerThanMinFileSize);
strictEqual(Object.keys(files).length, 0);
},
done
);
});

test("octet stream rejects an empty chunked body", (done) => {
upload(
{
port: 13554,
options: {},
headers: { "Transfer-Encoding": "chunked" },
body: Buffer.alloc(0),
},
(err, files) => {
assert(err, "expected an error for an empty octet-stream upload");
strictEqual(err.code, noEmptyFiles);
strictEqual(Object.keys(files).length, 0);
},
done
);
});

test("octet stream accepts an empty chunked body with allowEmptyFiles", (done) => {
upload(
{
port: 13555,
options: { allowEmptyFiles: true, minFileSize: 0 },
headers: { "Transfer-Encoding": "chunked" },
body: Buffer.alloc(0),
},
(err, files) => {
assert(!err, "an empty file is allowed with allowEmptyFiles");
strictEqual(files.file.length, 1);
strictEqual(files.file[0].size, 0);
},
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Resolved
done
);
});