diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9ca5480 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +examples/ +yarn.lock \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..ddffaf4 --- /dev/null +++ b/index.js @@ -0,0 +1,3 @@ +import parter from "./lib/parter.js"; + +export default parter; \ No newline at end of file diff --git a/lib/parter.js b/lib/parter.js new file mode 100644 index 0000000..87cf1b8 --- /dev/null +++ b/lib/parter.js @@ -0,0 +1,144 @@ +/* + * Author: Mr-M1M3 + * Project: busboy-promise + * Description: Turning an event based multipart parser, busboy to promise based + * Started Written: 5th July, 2022. + */ +import { + IncomingMessage +} from "http"; + +import {Buffer} from "buffer"; + +import busboy from "busboy"; + +export default async function parter(req, options) { + + if (options) { // if user passes options + + if (typeof options != 'object') { // it must be an object + + return Promise.reject(`Expected second parameter to be an object containing options for Busboy. Got ${typeof options}`); + + } + + } else { // if user doesn't passes any option + + options = {}; // replace it with an empty object + + } + + if (!options.headers) { // if options object doesn't contain headers + + options.headers = req.headers; // set option.headers to req.headers + + } + + const bb = busboy(options); // instantiate busboy + + if (req instanceof Request) { // if req is an instance of Request + + // pipe request body, a readable stream to busboy instance which is an writable stream. + //NOTE: body of Request instance is a readable stream and in nodejs readable stream `pipeTo` is not available. + //Rather, we use `pipe()`; + req.body.pipe(bb); + + + } else if (req instanceof IncomingMessage) { // if req is an instance of node IncomingMessage + + // nodejs IncomingMessage is a stream. + // So, it is possible to pipe the whole request object to another stream + req.pipe(bb); + + } else { // if req is invalid + + return Promise.reject("Expected first argument to be an instance of Request or IncomingMessage"); + + } + + function cleanUp() { + + } + + return new Promise((acc, rej) => { + + const data = { // an empty object to hold parsed data + files: {}, + fields: {} + } + + bb.on("error", busboyError); + bb.on("file", appendFile); // when a file is received, calls appendFile + bb.on("finish", finishParse); + + function appendFile(name, stream, info) { + // TODO: Process received file + + const bufferArray = []; // empty array to receive buffers + + + stream.on("error", streamError); // if any error happens, calls streamError function + stream.on("data", appendBuffer); // calls appendBuffer when a new chunk is received + stream.on("end", done); + + function appendBuffer(chunk){ // just pushes new chunk to `bufferArray` + bufferArray.push(chunk); + } + + function done(){ + let bufferLength = 0; // initially, total buffer length is assumed 0 + + for(let _buffer of bufferArray){ // determine the whole length of total buffers + bufferLength += _buffer.length; + } + + // finally + data.files[name] = {}; + data.files[name].content = Buffer.concat(bufferArray, bufferLength); + data.files[name].info = info; + + } + + function streamError(error) { + + streamCleanUp(); // removes all the listeners of the stream + + rej({ + + message: `an error happened while reading the stream of file named \`${name}\``, + error + + }); + + }; + + function streamCleanUp() { + cleanUp(); + stream.removeListener("data", appendBuffer); + stream.removeListener("end", done); + stream.removeListener("error", streamError); + } + } + + function finishParse(){ + acc(data); + } + + function busboyError(error) { // if any error happens + + cleanUp(); // 1. remove all listeners + + rej({ // rejects promise + message: "an error happened in busboy", + error + }) + } + + function cleanUp(){ + bb.removeListener("file", appendFile); + bb.removeListener("finish", finishParse); + bb.removeListener("error", busboyError); + } + }); + +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..29ec9ce --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "partrer", + "version": "0.0.1", + "description": "Promise based multipart parser", + "main": "index.js", + "repository": "https://github.com/obboy/parter", + "author": "m1m3@obboy", + "license": "MIT", + "dependencies": { + "busboy": "^1.6.0" + }, + "type": "module", + "devDependencies": { + "express": "^4.18.1", + "nodemon": "^2.0.18" + }, + "scripts": { + "test:dev": "nodemon examples/server.js" + } +}