Skip to content

readable.pipe(res) with Content-Length header causes HTTP/1.1 protocol violation #104

@VikramAditya33

Description

@VikramAditya33

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:

  1. Disable chunked transfer encoding
  2. Use uWS.tryEnd(chunk, totalSize) for each chunk
  3. 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

Metadata

Metadata

Labels

CRITICALCRITICAL bug/fix neededbig stuffProbably talking around a new feature or a request which is quite bigbugSomething isn't working

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions