summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcos Casagrande <marcos@denode.com>2023-10-05 19:28:44 +0200
committerGitHub <noreply@github.com>2023-10-05 19:28:44 +0200
commit176bf9ba5ffe1c925e7d6a395d0e946880afdcda (patch)
treeb3a9f3746e093802c8ad86c635afee436aa24758
parent1619932a651a189d590bd579f334faac3c6b4397 (diff)
fix(ext/formdata): support multiple headers in FormData (#20801)
Fixes https://github.com/denoland/deno/issues/20793
-rw-r--r--cli/tests/unit/body_test.ts32
-rw-r--r--ext/fetch/21_formdata.js31
2 files changed, 47 insertions, 16 deletions
diff --git a/cli/tests/unit/body_test.ts b/cli/tests/unit/body_test.ts
index 8aebfadd3..7dd026dc5 100644
--- a/cli/tests/unit/body_test.ts
+++ b/cli/tests/unit/body_test.ts
@@ -155,3 +155,35 @@ Deno.test(async function bodyArrayBufferMultipleParts() {
const body = buildBody(stream);
assertEquals((await body.arrayBuffer()).byteLength, size);
});
+
+// https://github.com/denoland/deno/issues/20793
+Deno.test(
+ { permissions: { net: true } },
+ async function bodyMultipartFormDataMultipleHeaders() {
+ const boundary = "----formdata-polyfill-0.970665446687947";
+ const payload = [
+ "------formdata-polyfill-0.970665446687947",
+ 'Content-Disposition: form-data; name="x"; filename="blob"',
+ "Content-Length: 1",
+ "Content-Type: application/octet-stream",
+ "last-modified: Wed, 04 Oct 2023 20:28:45 GMT",
+ "",
+ "y",
+ "------formdata-polyfill-0.970665446687947--",
+ ].join("\r\n");
+
+ const body = buildBody(
+ new TextEncoder().encode(payload),
+ new Headers({
+ "Content-Type": `multipart/form-data; boundary=${boundary}`,
+ }),
+ );
+
+ const formData = await body.formData();
+ const file = formData.get("x");
+ assert(file instanceof File);
+ const text = await file.text();
+ assertEquals(text, "y");
+ assertEquals(file.size, 1);
+ },
+);
diff --git a/ext/fetch/21_formdata.js b/ext/fetch/21_formdata.js
index 1f0f00088..d905ec7c4 100644
--- a/ext/fetch/21_formdata.js
+++ b/ext/fetch/21_formdata.js
@@ -448,25 +448,24 @@ class MultipartParser {
const prevByte = this.body[i - 1];
const isNewLine = byte === LF && prevByte === CR;
- if (state === 1 || state === 2 || state == 3) {
+ if (state === 1) {
headerText += StringFromCharCode(byte);
}
+
if (state === 0 && isNewLine) {
state = 1;
- } else if (state === 1 && isNewLine) {
- state = 2;
- const headersDone = this.body[i + 1] === CR &&
- this.body[i + 2] === LF;
-
- if (headersDone) {
- state = 3;
+ } else if (
+ state === 1
+ ) {
+ if (
+ isNewLine && this.body[i + 1] === CR &&
+ this.body[i + 2] === LF
+ ) {
+ // end of the headers section
+ state = 2;
+ fileStart = i + 3; // After \r\n
}
- } else if (state === 2 && isNewLine) {
- state = 3;
- } else if (state === 3 && isNewLine) {
- state = 4;
- fileStart = i + 1;
- } else if (state === 4) {
+ } else if (state === 2) {
if (this.boundaryChars[boundaryIndex] !== byte) {
boundaryIndex = 0;
} else {
@@ -487,7 +486,7 @@ class MultipartParser {
const latin1Filename = MapPrototypeGet(disposition, "filename");
const latin1Name = MapPrototypeGet(disposition, "name");
- state = 5;
+ state = 3;
// Reset
boundaryIndex = 0;
headerText = "";
@@ -510,7 +509,7 @@ class MultipartParser {
formData.append(name, core.decode(content));
}
}
- } else if (state === 5 && isNewLine) {
+ } else if (state === 3 && isNewLine) {
state = 1;
}
}