Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fetch fails with RequestContentLengthMismatchError when following redirect #2543

Closed
angelyan opened this issue Dec 25, 2023 · 3 comments · Fixed by #2545
Closed

fetch fails with RequestContentLengthMismatchError when following redirect #2543

angelyan opened this issue Dec 25, 2023 · 3 comments · Fixed by #2545
Labels
bug Something isn't working

Comments

@angelyan
Copy link
Contributor

Bug Description

fetch fails with a RequestContentLengthMismatchError error when following a redirect under the following conditions:

  • The request has a body.
  • The redirect preserves the body, e.g. 307 temporary redirects.
  • The redirect response doesn't end immediately after sending the headers.

Reproducible By

import { createServer } from 'http';
import { once } from 'events';
import { fetch } from 'undici';

async function main() {
  const server = createServer((req, res) => {
    if (req.url === '/redirect') {
      res.writeHead(307, { location: '/target' });
      res.write('flush headers');
      setTimeout(() => res.end(), 1000);
    } else {
      res.end();
    }
  }).listen(0).unref();

  await once(server, 'listening');

  await fetch(`http://localhost:${server.address().port}/redirect`, {
    method: 'POST',
    body: 'body',
  });
}

main();

fetch fails with the following error:

TypeError: fetch failed
    at fetch (/node_modules/undici/index.js:115:13)
    at async main (file://index.mjs:18:3) {
  cause: RequestContentLengthMismatchError: Request body length does not match content-length header
      at AsyncWriter.end (/node_modules/undici/lib/client.js:2257:15)
      at writeIterable (/node_modules/undici/lib/client.js:2140:12) {
    code: 'UND_ERR_REQ_CONTENT_LENGTH_MISMATCH'
  }
}

Environment

  • Node: 20.9.0
  • Undici: 6.2.1

Additional context

This happens because the connection is destroyed after a redirect is received:

fetchParams.controller.connection.destroy()

This causes the fetch to be aborted:

undici/lib/fetch/index.js

Lines 1721 to 1726 in d56cd7d

destroy (err) {
if (!this.destroyed) {
this.destroyed = true
this.abort?.(err ?? new DOMException('The operation was aborted.', 'AbortError'))
}
}

Afterwards, when the redirect is followed, the body is not written because the fetch was cancelled previously:

undici/lib/fetch/index.js

Lines 1833 to 1835 in d56cd7d

if (isCancelled(fetchParams)) {
return
}

This results in an error because the content-length and the written bytes do not match:

undici/lib/client.js

Lines 2255 to 2261 in 871baa7

if (contentLength !== null && bytesWritten !== contentLength) {
if (client[kStrictContentLength]) {
throw new RequestContentLengthMismatchError()
} else {
process.emitWarning(new RequestContentLengthMismatchError())
}
}

This doesn't happen when the redirect response ends immediately after sending the headers because the request is marked as completed before it's aborted, so the body is written as expected when the redirect is followed.

I was able to fix this by passing a flag to fetchParams.controller.connection.destroy() to destroy the connection without aborting the entire fetch, but I don't know if this is the right approach. If it is, I can send a PR.

@angelyan angelyan added the bug Something isn't working label Dec 25, 2023
@metcoder95
Copy link
Member

cc @KhafraDev

@KhafraDev
Copy link
Member

PR would be appreciated.

@angelyan
Copy link
Contributor Author

PR sent @KhafraDev: #2545

Kikobeats added a commit to Kikobeats/github-generate-release that referenced this issue Jan 8, 2024
There is a bug inside fetch fixed in coming undici version but still present in Node.js 18 & 20

nodejs/undici#2543
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants