Bug Description
Undici handles empty request bodies differently between HTTP/1.1 and HTTP/2 for methods that can carry payloads.
Reproducible By
import { createServer } from "node:http";
import { createSecureServer } from "node:http2";
import { once } from "node:events";
import { Client } from "undici";
import pem from "@metcoder95/https-pem";
const methods = ["QUERY", "PROPFIND", "PROPPATCH"];
async function collectBody(body) {
for await (const _ of body) {
// Drain the response body so the request completes.
}
}
async function requestH1(method) {
let receivedContentLength;
const server = createServer((req, res) => {
receivedContentLength = req.headers["content-length"];
req.resume();
res.end("ok");
});
await once(server.listen(0), "listening");
const client = new Client(`http://localhost:${server.address().port}`);
try {
const response = await client.request({ path: "/", method, body: "" });
await collectBody(response.body);
return receivedContentLength;
} finally {
await client.close();
server.close();
await once(server, "close");
}
}
async function requestH2(method) {
let receivedContentLength;
const server = createSecureServer(
await pem.generate({ opts: { keySize: 2048 } }),
);
server.on("stream", (stream, headers) => {
receivedContentLength = headers["content-length"];
stream.respond({ ":status": 200 });
stream.end("ok");
});
await once(server.listen(0), "listening");
const client = new Client(`https://localhost:${server.address().port}`, {
connect: { rejectUnauthorized: false },
});
try {
const response = await client.request({ path: "/", method, body: "" });
await collectBody(response.body);
return receivedContentLength;
} finally {
await client.close();
server.close();
await once(server, "close");
}
}
for (const method of methods) {
const h1ContentLength = await requestH1(method);
const h2ContentLength = await requestH2(method);
console.log(`${method}:`);
console.log(` H1 content-length: ${h1ContentLength}`);
console.log(` H2 content-length: ${h2ContentLength}`);
}
Expected Behavior
HTTP/1.1 and HTTP/2 should be consistent for empty payload-capable methods.
QUERY, PROPFIND, and PROPPATCH should be included in H2 expectsPayload, matching H1 behavior, so empty requests send content-length: 0 consistently.
Logs & Screenshots
QUERY:
H1 content-length: 0
H2 content-length: undefined
PROPFIND:
H1 content-length: 0
H2 content-length: undefined
PROPPATCH:
H1 content-length: 0
H2 content-length: undefined
Environment
macOS 26.4.1
Node v24.15.0
undici v8.2.0
Bug Description
Undici handles empty request bodies differently between HTTP/1.1 and HTTP/2 for methods that can carry payloads.
Reproducible By
Expected Behavior
HTTP/1.1 and HTTP/2 should be consistent for empty payload-capable methods.
QUERY,PROPFIND, andPROPPATCHshould be included in H2expectsPayload, matching H1 behavior, so empty requests sendcontent-length: 0consistently.Logs & Screenshots
Environment
macOS 26.4.1
Node v24.15.0
undici v8.2.0