Skip to content

Commit 4f13ac6

Browse files
Fix: Set Content-Length header explicitly for all request bodies (#831)
1 parent 18e439b commit 4f13ac6

File tree

3 files changed

+552
-12
lines changed

3 files changed

+552
-12
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@ This driver uses semantic versioning:
1414
- A change in the major version (e.g. 1.Y.Z -> 2.0.0) indicates _breaking_
1515
changes that require changes in your code to upgrade.
1616

17+
## [Unreleased]
18+
19+
### Fixed
20+
21+
- Fixed `ECONNRESET` errors in NextJS 15 production builds when using `next/cookies`
22+
by explicitly setting the `Content-Length` header for all fixed-size request bodies.
23+
The driver now calculates and sets `Content-Length` for strings (JSON and plain text),
24+
Blobs, Buffers, FormData, and empty bodies, ensuring compatibility with NextJS 15
25+
dynamic routes and other environments that don't automatically set this header.
26+
([#831](https://github.com/arangodb/arangojs/issues/831))
27+
1728
## [10.2.0] - 2026-01-16
1829

1930
### Fixed

src/connection.ts

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,21 +1166,75 @@ export class Connection {
11661166
// Workaround for ArangoDB 3.12.0-rc1 and earlier:
11671167
// Omitting the final CRLF results in "bad request body" fatal error
11681168
body = new Blob([blob, "\r\n"], { type: blob.type });
1169-
} else if (body) {
1170-
let contentType;
1171-
if (isBinary) {
1172-
contentType = "application/octet-stream";
1173-
} else if (typeof body === "object") {
1174-
body = JSON.stringify(body);
1175-
contentType = "application/json";
1176-
} else {
1177-
body = String(body);
1178-
contentType = "text/plain";
1169+
// Set content-length for Blob (needed for Next.js dynamic routes)
1170+
if (!headers.has("content-length")) {
1171+
headers.set("content-length", String(body.size));
11791172
}
1180-
if (!headers.has("content-type")) {
1181-
headers.set("content-type", contentType);
1173+
} else if (body !== null && body !== undefined) {
1174+
// Handle empty string explicitly
1175+
if (body === "") {
1176+
if (!headers.has("content-length")) {
1177+
headers.set("content-length", "0");
1178+
}
1179+
if (!headers.has("content-type")) {
1180+
headers.set("content-type", "text/plain");
1181+
}
1182+
} else {
1183+
let contentType;
1184+
if (isBinary) {
1185+
contentType = "application/octet-stream";
1186+
} else if (typeof body === "object") {
1187+
body = JSON.stringify(body);
1188+
contentType = "application/json";
1189+
} else {
1190+
body = String(body);
1191+
contentType = "text/plain";
1192+
}
1193+
if (!headers.has("content-type")) {
1194+
headers.set("content-type", contentType);
1195+
}
1196+
// Explicitly set content-length for fixed-size bodies
1197+
// This is needed for Next.js dynamic routes (e.g., when using cookies())
1198+
// and ensures compatibility with environments that don't set it automatically
1199+
if (!headers.has("content-length")) {
1200+
if (typeof body === "string") {
1201+
// Calculate byte length for UTF-8 encoded strings
1202+
// Buffer is available in Node.js (required: >=20) and browser polyfills
1203+
if (
1204+
typeof globalThis.Buffer !== "undefined" &&
1205+
globalThis.Buffer.byteLength
1206+
) {
1207+
const contentLength = globalThis.Buffer.byteLength(body, "utf8");
1208+
headers.set("content-length", String(contentLength));
1209+
} else {
1210+
// Fallback: use TextEncoder for browser environments
1211+
const encoder = new TextEncoder();
1212+
const contentLength = encoder.encode(body).length;
1213+
headers.set("content-length", String(contentLength));
1214+
}
1215+
} else if (body instanceof Blob) {
1216+
headers.set("content-length", String(body.size));
1217+
} else if (
1218+
typeof globalThis.Buffer !== "undefined" &&
1219+
globalThis.Buffer.isBuffer &&
1220+
globalThis.Buffer.isBuffer(body)
1221+
) {
1222+
headers.set("content-length", String(body.length));
1223+
}
1224+
// Note: ArangoDB does not support Transfer-Encoding: chunked.
1225+
// All public APIs only accept fixed-size bodies (string, Buffer, Blob, FormData, or objects that get JSON.stringify'd),
1226+
// so streams should never reach this point. If an unknown body type is passed, it will be converted to string above.
1227+
}
11821228
}
11831229
}
1230+
// Handle null/undefined body for POST/PUT/PATCH methods that might need content-length: 0
1231+
// This ensures compatibility with NextJS 15 and other environments that expect explicit content-length
1232+
else if (
1233+
(method === "POST" || method === "PUT" || method === "PATCH") &&
1234+
!headers.has("content-length")
1235+
) {
1236+
headers.set("content-length", "0");
1237+
}
11841238

11851239
if (this._transactionId) {
11861240
headers.set("x-arango-trx-id", this._transactionId);

0 commit comments

Comments
 (0)