Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BREAKING(mime/multipart): return array for multiple values with same form name #722

Merged
merged 9 commits into from
Jul 6, 2021
Merged
Show file tree
Hide file tree
Changes from 4 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
54 changes: 25 additions & 29 deletions mime/multipart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,13 +245,13 @@ function skipLWSPChar(u: Uint8Array): Uint8Array {
}

export interface MultipartFormData {
file(key: string): FormFile | FormFile[] | undefined;
value(key: string): string | undefined;
files(key: string): FormFile[] | undefined;
values(key: string): string[] | undefined;
kt3k marked this conversation as resolved.
Show resolved Hide resolved
entries(): IterableIterator<
[string, string | FormFile | FormFile[] | undefined]
[string, string[] | FormFile[] | undefined]
>;
[Symbol.iterator](): IterableIterator<
[string, string | FormFile | FormFile[] | undefined]
[string, string[] | FormFile[] | undefined]
>;
/** Remove all tempfiles */
removeAll(): Promise<void>;
Expand All @@ -277,8 +277,8 @@ export class MultipartReader {
* @param maxMemory maximum memory size to store file in memory. bytes. @default 10485760 (10MB)
* */
async readForm(maxMemory = 10 << 20): Promise<MultipartFormData> {
const fileMap = new Map<string, FormFile | FormFile[]>();
const valueMap = new Map<string, string>();
const fileMap = new Map<string, FormFile[]>();
const valueMap = new Map<string, string[]>();
let maxValueBytes = maxMemory + (10 << 20);
const buf = new Deno.Buffer(new Uint8Array(maxValueBytes));
for (;;) {
Expand All @@ -298,7 +298,12 @@ export class MultipartReader {
throw new RangeError("message too large");
}
const value = new TextDecoder().decode(buf.bytes());
valueMap.set(p.formName, value);
const mapVal = valueMap.get(p.formName);
if (mapVal !== undefined) {
mapVal.push(value);
} else {
valueMap.set(p.formName, [value]);
}
continue;
}
// file
Expand Down Expand Up @@ -344,13 +349,9 @@ export class MultipartReader {
if (formFile) {
const mapVal = fileMap.get(p.formName);
if (mapVal !== undefined) {
if (Array.isArray(mapVal)) {
mapVal.push(formFile);
} else {
fileMap.set(p.formName, [mapVal, formFile]);
}
mapVal.push(formFile);
} else {
fileMap.set(p.formName, formFile);
fileMap.set(p.formName, [formFile]);
}
}
}
Expand Down Expand Up @@ -419,43 +420,38 @@ export class MultipartReader {
}

function multipartFormData(
fileMap: Map<string, FormFile | FormFile[]>,
valueMap: Map<string, string>,
fileMap: Map<string, FormFile[]>,
valueMap: Map<string, string[]>,
): MultipartFormData {
function file(key: string): FormFile | FormFile[] | undefined {
function files(key: string): FormFile[] | undefined {
return fileMap.get(key);
}
function value(key: string): string | undefined {
function values(key: string): string[] | undefined {
return valueMap.get(key);
}
function* entries(): IterableIterator<
[string, string | FormFile | FormFile[] | undefined]
[string, string[] | FormFile[] | undefined]
> {
yield* fileMap;
yield* valueMap;
}
async function removeAll(): Promise<void> {
const promises: Array<Promise<void>> = [];
for (const val of fileMap.values()) {
if (Array.isArray(val)) {
for (const subVal of val) {
if (!subVal.tempfile) continue;
promises.push(Deno.remove(subVal.tempfile));
}
} else {
if (!val.tempfile) continue;
promises.push(Deno.remove(val.tempfile));
for (const subVal of val) {
if (!subVal.tempfile) continue;
promises.push(Deno.remove(subVal.tempfile));
}
}
await Promise.all(promises);
}
return {
file,
value,
files,
values,
entries,
removeAll,
[Symbol.iterator](): IterableIterator<
[string, string | FormFile | FormFile[] | undefined]
[string, string[] | FormFile[] | undefined]
> {
return entries();
},
Expand Down
30 changes: 13 additions & 17 deletions mime/multipart_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,12 +183,13 @@ Deno.test({
"--------------------------434049563556637648550474",
);
const form = await mr.readForm();
assertEquals(form.value("foo"), "foo");
assertEquals(form.value("bar"), "bar");
const file = form.file("file");
assertEquals(form.values("foo"), ["foo"]);
assertEquals(form.values("bar"), ["bar"]);
assertEquals(form.values("baz"), ["str1", "str2"]);
const [file] = form.files("file") || [];
assert(isFormFile(file));
assert(file.content !== void 0);
const file2 = form.file("file2");
const [file2] = form.files("file2") || [];
assert(isFormFile(file2));
assert(file2.filename === "中文.json");
assert(file2.content !== void 0);
Expand Down Expand Up @@ -226,12 +227,9 @@ Deno.test({
// use low-memory to write "file" into temp file.
const form = await mr.readForm(20);
try {
assertEquals(form.value("deno"), "land");
assertEquals(form.value("bar"), "bar");
let file = form.file("file");
if (Array.isArray(file)) {
file = file[0];
}
assertEquals(form.values("deno"), ["land"]);
assertEquals(form.values("bar"), ["bar"]);
const [file] = form.files("file") || [];
assert(file != null);
assert(file.tempfile != null);
assertEquals(file.size, size);
Expand All @@ -255,10 +253,7 @@ Deno.test({
"--------------------------434049563556637648550474",
);
const form = await mr.readForm(20);
let file = form.file("file");
if (Array.isArray(file)) {
file = file[0];
}
const [file] = form.files("file") || [];
assert(file != null);
const { tempfile, content } = file;
assert(tempfile != null);
Expand All @@ -283,9 +278,10 @@ Deno.test({
);
const form = await mr.readForm();
const map = new Map(form.entries());
assertEquals(map.get("foo"), "foo");
assertEquals(map.get("bar"), "bar");
const file = map.get("file");
assertEquals(map.get("foo"), ["foo"]);
assertEquals(map.get("bar"), ["bar"]);
assertEquals(map.get("baz"), ["str1", "str2"]);
const [file] = map.get("file") || [];
assert(isFormFile(file));
assertEquals(file.filename, "tsconfig.json");
o.close();
Expand Down
10 changes: 10 additions & 0 deletions mime/testdata/sample.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,14 @@ content-type: application/octet-stream
"test": "filename"
}

----------------------------434049563556637648550474
content-disposition: form-data; name="baz"
content-type: application/octet-stream

str1
----------------------------434049563556637648550474
content-disposition: form-data; name="baz"
content-type: application/octet-stream

str2
----------------------------434049563556637648550474--