Fix MultipartReader for big files (#4865)

This commit is contained in:
Marcos Casagrande 2020-04-29 22:38:24 +02:00 committed by GitHub
parent d308e8d0c0
commit 78e0ae643c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 34 additions and 21 deletions

View file

@ -308,7 +308,7 @@ export class MultipartReader {
} }
// file // file
let formFile: FormFile | undefined; let formFile: FormFile | undefined;
const n = await copy(p, buf); const n = await copyN(p, buf, maxValueBytes);
const contentType = p.headers.get("content-type"); const contentType = p.headers.get("content-type");
assert(contentType != null, "content-type must be set"); assert(contentType != null, "content-type must be set");
if (n > maxMemory) { if (n > maxMemory) {
@ -319,11 +319,8 @@ export class MultipartReader {
postfix: ext, postfix: ext,
}); });
try { try {
const size = await copyN( const size = await copy(new MultiReader(buf, p), file);
new MultiReader(buf, p),
file,
maxValueBytes
);
file.close(); file.close();
formFile = { formFile = {
filename: p.fileName, filename: p.fileName,
@ -333,6 +330,7 @@ export class MultipartReader {
}; };
} catch (e) { } catch (e) {
await remove(filepath); await remove(filepath);
throw e;
} }
} else { } else {
formFile = { formFile = {

View file

@ -1,6 +1,6 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
const { Buffer, copy, open, test } = Deno; const { Buffer, open, test } = Deno;
import { import {
assert, assert,
assertEquals, assertEquals,
@ -21,6 +21,7 @@ const e = new TextEncoder();
const boundary = "--abcde"; const boundary = "--abcde";
const dashBoundary = e.encode("--" + boundary); const dashBoundary = e.encode("--" + boundary);
const nlDashBoundary = e.encode("\r\n--" + boundary); const nlDashBoundary = e.encode("\r\n--" + boundary);
const testdataDir = path.resolve("mime", "testdata");
test("multipartScanUntilBoundary1", function (): void { test("multipartScanUntilBoundary1", function (): void {
const data = `--${boundary}`; const data = `--${boundary}`;
@ -192,29 +193,43 @@ test({
}); });
test({ test({
name: "[mime/multipart] readForm() should store big file in temp file", name:
"[mime/multipart] readForm() should store big file completely in temp file",
async fn() { async fn() {
const o = await open(path.resolve("./mime/testdata/sample.txt")); const multipartFile = path.join(testdataDir, "form-data.dat");
const mr = new MultipartReader( const sampleFile = await Deno.makeTempFile();
o, const writer = await open(multipartFile, { write: true, create: true });
"--------------------------434049563556637648550474"
); const size = 1 << 24; // 16mb
await Deno.truncate(sampleFile, size);
const bigFile = await open(sampleFile, { read: true });
const mw = new MultipartWriter(writer);
await mw.writeField("deno", "land");
await mw.writeField("bar", "bar");
await mw.writeFile("file", "sample.bin", bigFile);
await mw.close();
writer.close();
bigFile.close();
const o = await Deno.open(multipartFile);
const mr = new MultipartReader(o, mw.boundary);
// use low-memory to write "file" into temp file. // use low-memory to write "file" into temp file.
const form = await mr.readForm(20); const form = await mr.readForm(20);
try { try {
assertEquals(form.value("foo"), "foo"); assertEquals(form.value("deno"), "land");
assertEquals(form.value("bar"), "bar"); assertEquals(form.value("bar"), "bar");
const file = form.file("file"); const file = form.file("file");
assert(file != null); assert(file != null);
assertEquals(file.type, "application/octet-stream");
assert(file.tempfile != null); assert(file.tempfile != null);
const f = await open(file.tempfile); assertEquals(file.size, size);
const w = new StringWriter(); assertEquals(file.type, "application/octet-stream");
await copy(f, w); // TODO checksum of tmp & sampleFile
const json = JSON.parse(w.toString());
assertEquals(json["compilerOptions"]["target"], "es2018");
f.close();
} finally { } finally {
await Deno.remove(multipartFile);
await Deno.remove(sampleFile);
await form.removeAll(); await form.removeAll();
o.close(); o.close();
} }