-
-
Notifications
You must be signed in to change notification settings - Fork 35k
Open
Labels
httpIssues or PRs related to the http subsystem.Issues or PRs related to the http subsystem.streamIssues and PRs related to the stream subsystem.Issues and PRs related to the stream subsystem.
Description
Version
v22.14.0
Platform
Linux Sebastian 6.6.87.2-microsoft-standard-WSL2 #1 SMP PREEMPT_DYNAMIC Thu Jun 5 18:30:46 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
Subsystem
http, stream
What steps will reproduce the bug?
finished fails to obtain the OutgoingMessage when the request encounters an error on the same socket, resulting in a situation where it is never known whether the OutgoingMessage has completed, via stream.finished
const http = require('http')
const net = require('net')
const assert = require('assert')
const { finished } = require('stream')
let count = 0
const responses = []
const server = http.createServer(function (req, res) {
responses.push(res)
finished(res, function (err) {
console.log('finished res ' + req.url)
assert.ok(err)
console.log(err.code)
assert.strictEqual(err.code, 'ERR_STREAM_PREMATURE_CLOSE')
assert.strictEqual(responses[0], res)
responses.shift()
if (responses.length === 0) {
socket.end()
return
}
responses[0].end('response b')
})
finished(req, function (err) {
console.log('finished req ' + req.url)
if (req.url === '/1') {
assert.ifError(err)
} else {
assert.ok(err)
console.log(err.code)
assert.strictEqual(err.code, 'ECONNRESET')
}
if (++count !== 2) {
return
}
assert.strictEqual(responses.length, 1)
})
if (responses.length === 1) {
// second request
writeRequest(socket, true, '/2')
socket.write('2')
}
req.resume()
})
let socket
server.listen(function () {
var data = ''
socket = net.connect(this.address().port, function () {
writeRequest(this, false, '/1')
})
socket.on('data', function (chunk) {
data += chunk.toString('binary')
})
socket.on('end', function () {
console.log("Data: ", data)
assert.strictEqual(count, 2)
assert.strictEqual(responses.length, 0)
server.close(done)
})
})
function writeRequest(socket, chunked, path) {
socket.write('GET ' + (path || '/') + ' HTTP/1.1\r\n')
socket.write('Host: localhost\r\n')
socket.write('Connection: keep-alive\r\n')
if (chunked) {
socket.write('Transfer-Encoding: chunked\r\n')
}
socket.write('\r\n')
}For example, when a malformed request is sent without HTTP pipelining, finished is able to be called.
const http = require('http')
const net = require('net')
const { finished } = require('stream')
const server = http.createServer(function (req, res) {
finished(res, function () {
console.log('finished res ' + req.url)
server.close()
})
finished(req, function () {
console.log('finished req ' + req.url)
})
socket.write('W')
})
let socket
server.listen(function () {
socket = net.connect(this.address().port, function () {
writeRequest(this)
})
})
function writeRequest(socket) {
socket.write('GET / HTTP/1.1\r\n')
socket.write('Host: localhost\r\n')
socket.write('Connection: keep-alive\r\n')
socket.write('Transfer-Encoding: chunked\r\n')
socket.write('\r\n')
}How often does it reproduce? Is there a required condition?
Always
What is the expected behavior? Why is that the expected behavior?
finished can be called when the same socket is used a second or multiple times and that request is malformed
What do you see instead?
Additional information
No response
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
httpIssues or PRs related to the http subsystem.Issues or PRs related to the http subsystem.streamIssues and PRs related to the stream subsystem.Issues and PRs related to the stream subsystem.