Skip to content

Commit 4d31988

Browse files
committed
fix(form-data): Use GetDeflatedUTF8StringLength to calculate encoded length
1 parent 50f0625 commit 4d31988

File tree

2 files changed

+32
-15
lines changed

2 files changed

+32
-15
lines changed

builtins/web/form-data/form-data-encoder.cpp

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "../streams/native-stream-source.h"
1010

1111
#include "encode.h"
12+
#include "jstypes.h"
1213
#include "mozilla/Assertions.h"
1314
#include "mozilla/ResultVariant.h"
1415

@@ -42,6 +43,35 @@ size_t compute_normalized_len(std::string_view src) {
4243
return len;
4344
}
4445

46+
size_t compute_unencoded_normalized_len(JSContext *cx, JS::HandleString value) {
47+
auto linear = JS_EnsureLinearString(cx, value);
48+
auto len = JS::GetDeflatedUTF8StringLength(linear);
49+
size_t js_len = JS_GetStringLength(value);
50+
51+
for (size_t i = 0; i < js_len; i++) {
52+
char16_t ch = 0;
53+
if (!JS_GetStringCharAt(cx, value, i, &ch)) {
54+
break;
55+
}
56+
if (ch == CR) {
57+
if (i+1 < js_len) {
58+
char16_t next = 0;
59+
if (JS_GetStringCharAt(cx, value, i + 1, &next)) {
60+
if (next == LF) {
61+
i += 1;
62+
// the character is already accounted for
63+
continue;
64+
}
65+
}
66+
}
67+
len += 1;
68+
} else if (ch == LF) {
69+
len += 1; // CRLF
70+
}
71+
}
72+
return len;
73+
}
74+
4575
// Normalizes newlines in a string by replacing:
4676
// - CR not followed by LF -> CRLF
4777
// - LF not preceded by CR -> CRLF
@@ -517,12 +547,11 @@ mozilla::Result<size_t, OutOfMemory> MultipartFormDataImpl::query_length(JSConte
517547
total += 2 * crlf_len;
518548

519549
RootedValue value_str(cx, entry.value);
520-
auto value = core::encode(cx, value_str);
550+
JS::RootedString value(cx, JS::ToString(cx, value_str));
521551
if (!value) {
522552
return mozilla::Result<size_t, OutOfMemory>(OutOfMemory {});
523553
}
524-
525-
total += compute_normalized_len(value);
554+
total += compute_unencoded_normalized_len(cx, value);
526555
} else {
527556
MOZ_ASSERT(File::is_instance(entry.value));
528557
RootedObject obj(cx, &entry.value.toObject());

tests/integration/formData/formData.js

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { serveTest } from "../test-server.js";
22
import { strictEqual, assert } from "../../assert.js";
33

4-
5-
64
export const handler = serveTest(async (t) => {
75
await t.test("encode-form-data-simple", async () => {
86
let fd = new FormData();
@@ -12,8 +10,6 @@ export const handler = serveTest(async (t) => {
1210
method: "POST",
1311
body: fd,
1412
});
15-
let bytes = await req.text();
16-
console.log(bytes);
1713
strictEqual(req.headers.get("content-length"), "250")
1814
assert(req.headers.get("content-type")?.startsWith("multipart/form-data;"));
1915
});
@@ -24,9 +20,6 @@ export const handler = serveTest(async (t) => {
2420
method: "POST",
2521
body: fd,
2622
});
27-
"".charCodeAt
28-
let bytes = await req.text();
29-
console.log(bytes);
3023
strictEqual(req.headers.get("content-length"), "177")
3124
assert(req.headers.get("content-type")?.startsWith("multipart/form-data;"));
3225
});
@@ -38,8 +31,6 @@ export const handler = serveTest(async (t) => {
3831
method: "POST",
3932
body: fd,
4033
});
41-
let bytes = await req.text();
42-
console.log(bytes);
4334
strictEqual(req.headers.get("content-length"), "277")
4435
assert(req.headers.get("content-type")?.startsWith("multipart/form-data;"));
4536
});
@@ -55,9 +46,6 @@ export const handler = serveTest(async (t) => {
5546
method: "POST",
5647
body: fd,
5748
});
58-
// ensure the body is encoded
59-
await req.text();
60-
// console.log(bytes);
6149
strictEqual(req.headers.get("content-length"), "1231")
6250
assert(req.headers.get("content-type")?.startsWith("multipart/form-data;"));
6351
});

0 commit comments

Comments
 (0)