When using standard Node.js streaming (fs.createReadStream().pipe(res)) with a manually-set Content-Length header, uWebSockets.js sends both Content-Length and Transfer-Encoding: chunked, which violates HTTP/1.1 (RFC 7230 Section 3.3.2). This causes strict HTTP clients (Node.js fetch/undici) to reject the response with:
HTTPParserError: Response does not match the HTTP/1.1 protocol
(Transfer-Encoding can't be present with Content-Length)
UwsResponse._write() always calls streamChunk(buffer) without passing totalSize, which means it always uses uWS.write() (chunked encoding mode). Even when the user sets res.setHeader('content-length', '1048576'), the underlying uWS response still uses chunked transfer encoding because _write() never switches to tryEnd() mode.
Expected Behavior
Setting Content-Length before piping should:
- Disable chunked transfer encoding
- Use
uWS.tryEnd(chunk, totalSize) for each chunk
- Allow clients to receive a valid HTTP/1.1 response with only
Content-Length
Reproduction
@Controller('test')
class TestController {
@Get('pipe-with-length')
async pipeWithLength(@Res() res: UwsResponse) {
const filePath = 'test-file.bin';
const stats = fs.statSync(filePath);
res.setHeader('content-length', String(stats.size));
fs.createReadStream(filePath).pipe(res);
}
}
Client:
const response = await fetch('http://localhost:3000/test/pipe-with-length');
// Throws: HTTPParserError: Transfer-Encoding can't be present with Content-Length
When using standard Node.js streaming (
fs.createReadStream().pipe(res)) with a manually-setContent-Lengthheader, uWebSockets.js sends bothContent-LengthandTransfer-Encoding: chunked, which violates HTTP/1.1 (RFC 7230 Section 3.3.2). This causes strict HTTP clients (Node.js fetch/undici) to reject the response with:UwsResponse._write()always callsstreamChunk(buffer)without passingtotalSize, which means it always usesuWS.write()(chunked encoding mode). Even when the user setsres.setHeader('content-length', '1048576'), the underlying uWS response still uses chunked transfer encoding because_write()never switches totryEnd()mode.Expected Behavior
Setting
Content-Lengthbefore piping should:uWS.tryEnd(chunk, totalSize)for each chunkContent-LengthReproduction
Client: