Skip to content

fix: give an empty request body the shape express gives it - #371

Closed
nigrosimone wants to merge 1 commit into
dimdenGD:mainfrom
nigrosimone:fix/empty-body-parsers
Closed

fix: give an empty request body the shape express gives it#371
nigrosimone wants to merge 1 commit into
dimdenGD:mainfrom
nigrosimone:fix/empty-body-parsers

Conversation

@nigrosimone

@nigrosimone nigrosimone commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

A request with content-length: 0 returned early before any parser ran, leaving req.body as the placeholder object every parser starts from. Express runs the parser regardless, and each one produces its own empty value.

app.post('/upload', express.raw(), (req, res) => {
    hash.update(req.body);   // Buffer on express, plain object here -> throws
});

Same for express.text(), where express hands back '' and this handed back an object.

parser express 4 express 5 uExpress before uExpress after
json {} {} {} {}
urlencoded {} {} {} {}
text '' '' {} ''
raw empty Buffer empty Buffer {} empty Buffer

json and urlencoded happened to line up because the placeholder is object-shaped; text and raw did not.

What changed

The empty case now runs the parser tail directly with an empty buffer instead of returning early, and it does so after the content-type checks, so a parser still ignores a request it does not handle. Nothing is read, so the cost is one zero-length buffer allocation on a path that previously did nothing.

All four tails already handled an empty buffer — json even has an explicit if(buf.length === 0) branch — so each one produces what it should without further changes.

One thing deliberately left alone

When a parser does not claim a request, express 4 leaves req.body as {} and express 5 leaves it undefined. uExpress follows express 4, which is what the suite enforces. That divergence predates this change and is not touched here — the test avoids that case so it can pass cleanly against both express versions.

A request with content-length: 0 returned early before any parser ran, leaving
req.body as the placeholder object every parser starts from. Express runs the
parser regardless and each one produces its own empty value, so

    app.post('/upload', express.raw(), (req, res) => {
        hash.update(req.body);   // works on express, throws on uExpress
    })

got a Buffer on express and a plain object here. Same for express.text(), where
express hands back '' and this handed back an object.

The empty case now runs the parser tail directly with an empty buffer, after the
content-type checks so a parser still ignores a request it does not handle.
Nothing is read, so this costs an allocation of a zero-length buffer. All four
tails already handled an empty buffer - json even has an explicit branch for it -
so each one produces what it should: {} for json and urlencoded, '' for text, an
empty Buffer for raw.

Verified against express 4 and express 5, which agree with each other here, and
the new test now matches both.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants