Skip to content

Commit

Permalink
fix: multipart parser
Browse files Browse the repository at this point in the history
  • Loading branch information
izatop committed Dec 1, 2022
1 parent a8b5d09 commit 19f35d3
Show file tree
Hide file tree
Showing 9 changed files with 669 additions and 524 deletions.
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@
"license": "MIT",
"private": true,
"devDependencies": {
"@commitlint/cli": "^17.2.0",
"@commitlint/config-conventional": "^17.2.0",
"@commitlint/cli": "^17.3.0",
"@commitlint/config-conventional": "^17.3.0",
"@types/jest": "^29.2.3",
"@types/node": "^18.11.9",
"@typescript-eslint/eslint-plugin": "^5.43.0",
"@typescript-eslint/parser": "^5.43.0",
"@types/node": "^18.11.10",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
"cross-env": "^7.0.3",
"eslint": "^8.27.0",
"eslint": "^8.28.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-unused-imports": "^2.0.0",
"jest": "^29.3.1",
Expand All @@ -37,6 +37,6 @@
"typescript": "^4.9.3"
},
"dependencies": {
"lerna": "^6.0.3"
"lerna": "^6.1.0"
}
}
2 changes: 2 additions & 0 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
"@bunt/input": "^0.24.38",
"@bunt/unit": "^0.24.45",
"@bunt/util": "^0.24.38",
"@types/busboy": "^1.5.0",
"busboy": "^1.6.0",
"path-to-regexp": "^6.2.1"
},
"license": "MIT"
Expand Down
17 changes: 15 additions & 2 deletions packages/app/src/Request/RequestAbstract.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import {ILogable, isFunction, Promisify} from "@bunt/util";
import {Application} from "../Application";
import {IHeaders, IKeyValueMap, IRequest, IRequestTransform, RequestTransformType} from "../interfaces";
import {fromJsonRequest, fromTextRequest} from "../TransformRequest";
import {
fromJsonRequest,
fromTextRequest,
MultipartFormDataTransform,
URLEncodedFormTransform,
} from "../TransformRequest";

export abstract class RequestAbstract implements IRequest, ILogable<{route: string}> {
public abstract readonly route: string;
Expand Down Expand Up @@ -46,9 +51,17 @@ export abstract class RequestAbstract implements IRequest, ILogable<{route: stri
}

/**
* Serialize a request body to JSON.
* Serialize the request body to object.
*/
public toObject<T = unknown>(): Promise<T> {
const [contentType] = this.headers.get("content-type").split(";");
switch(contentType) {
case "multipart/form-data":
return this.transform<T>(MultipartFormDataTransform);
case "application/x-www-form-urlencoded":
return this.transform<T>(URLEncodedFormTransform);
}

return this.to(fromJsonRequest) as Promise<T>;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/TransformRequest/JSONTransform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {IRequest, IRequestTransform} from "../interfaces";
export const JSONTransform = async <T>(request: IRequest): Promise<T> => {
request.headers.assert("content-type", ["application/json"]);
const buffer = await request.getBuffer();

return JSON.parse(buffer.toString("utf-8"));
};

Expand Down
54 changes: 54 additions & 0 deletions packages/app/src/TransformRequest/MultipartFormDataTransform.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {randomBytes} from "crypto";
import {tmpdir} from "os";
import {join} from "path";
import {createWriteStream} from "fs";
import {Defer} from "@bunt/util";
import busboy from "busboy";
import {IRequest} from "../interfaces";

export const MultipartFormDataTransform = async <T = unknown>(request: IRequest): Promise<T> => {
request.headers.assert("Content-Type", (value) => value.startsWith("multipart/form-data"));
const bb = busboy({headers: request.headers.toJSON()});
const rs = await request.createReadableStream();
const defer = new Defer<unknown>();
const result: Record<string, any> = {};
const pending: Defer<void>[] = [];

bb
.on("file", (name, file, info) => {
const {encoding, filename, mimeType} = info;
const tmpname = join(
tmpdir(),
randomBytes(4).toString("hex"),
Buffer.from(filename, "utf-8").toString("hex"),
);

const value = {
filename,
encoding,
mimeType,
tmpname,
};

if (name.endsWith("[]")) {
result[name] = [...(result[name] ?? []), value];
} else {
result[name] = value;
}

const def = new Defer<void>();
pending.push(def);

file
.pipe(createWriteStream(tmpname))
.on("close", () => def.resolve());
})
.on("field", (name, value) => result[name] = value)
.on("close", () => defer.resolve(result));

rs.pipe(bb);

await Promise.all(pending);

return defer.then((res) => res as T);
};
8 changes: 4 additions & 4 deletions packages/app/src/TransformRequest/URLEncodedFormTransform.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {parse, ParsedUrlQuery} from "querystring";
import {parse} from "querystring";
import {IRequest} from "../interfaces";

export const URLEncodedFormTransform = async (request: IRequest): Promise<ParsedUrlQuery> => {
export const URLEncodedFormTransform = async <T = unknown>(request: IRequest): Promise<T> => {
request.headers.assert("Content-Type", "application/x-www-form-urlencoded");
const buffer = await request.getBuffer();
return parse(buffer.toString("utf-8"));

return parse(buffer.toString("utf-8")) as any;
};
1 change: 1 addition & 0 deletions packages/app/src/TransformRequest/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./URLEncodedFormTransform";
export * from "./JSONTransform";
export * from "./MultipartFormDataTransform";
2 changes: 1 addition & 1 deletion packages/fs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"dependencies": {
"@bunt/unit": "^0.24.45",
"@bunt/util": "^0.24.38",
"@types/minio": "^7.0.14",
"@types/minio": "^7.0.15",
"minio": "^7.0.32"
},
"license": "MIT"
Expand Down
Loading

0 comments on commit 19f35d3

Please sign in to comment.