http-server icon indicating copy to clipboard operation
http-server copied to clipboard

`response.writeHead()` results in stalled response

Open vocafeuvre opened this issue 1 year ago • 0 comments

Package version

@adonisjs/[email protected]

Describe the bug

Calling response.writeHead() when using response.stream() prevents the stream from being piped to the response body.

Cause of the issue

In line 1113 of file src/response.ts, a check against response.isPending is made before the stream is about to be piped to the body in response.streamBody(); if response.isPending is false, the finish() method returns and never reaches response.streamBody().

If response.writeHead() is called anywhere before a response.finish call (implicit or explicit), it will cause response.headersSent to be true, and since response.isPending = !response.headersSent && !response.finished, response.isPending will be false.

Why this is a bug

In my real-world app, I have to stream out a CSV file without knowing its length beforehand, and hence I cannot set the Content-Length header. This means, I have to set the Transfer-Encoding: chunked header, and write the response headers as soon as possible. This bug prevents me from doing that without resorting to a workaround.

My workaround is to force the piping of the stream to the response body by copying the contents of response.streamBody() and placing them in my controller function. Thankfully, the AdonisJS Response object exposes the underlying Node.js ServerResponse object, which makes the forcing of the pipe possible.

Reproduction

I created a reproduction using the starter web kit. In it, I have a web page at / endpoint, which contains two buttons Errored Dump and Dump. Errored Dump calls the /dump-error endpoint, which reproduces the bug. Dump calls the /dump endpoint, which shows my workaround as working.

Fix Proposal

The simplest way to fix this would be to add a response.writeHeadCalled flag, which will cancel out response.headersSent like this: response.isPending = !(response.writeHeadCalled ? false : response.headersSent) && !response.finished.

Reproduction repo

https://github.com/vocafeuvre/write-head-issue-repro

vocafeuvre avatar May 07 '24 09:05 vocafeuvre