Skip to content

Commit d7aa006

Browse files
cody-greenethijs
authored and
thijs
committed
fix: double client.end() hang (brianc#2717)
* fix: double client.end() hang fixes brianc#2716 `client.end()` will resolve early if the connection is already dead, rather than waiting for an "end" event that will never arrive. * fix: client.end() resolves when socket is fully closed
1 parent ab04c47 commit d7aa006

File tree

3 files changed

+41
-4
lines changed

3 files changed

+41
-4
lines changed

packages/pg/lib/client.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class Client extends EventEmitter {
3737
this._Promise = c.Promise || global.Promise
3838
this._types = new TypeOverrides(c.types)
3939
this._ending = false
40+
this._ended = false
4041
this._connecting = false
4142
this._connected = false
4243
this._connectionError = false
@@ -144,6 +145,7 @@ class Client extends EventEmitter {
144145

145146
clearTimeout(this.connectionTimeoutHandle)
146147
this._errorAllQueries(error)
148+
this._ended = true
147149

148150
if (!this._ending) {
149151
// if the connection is ended without us calling .end()
@@ -630,7 +632,7 @@ class Client extends EventEmitter {
630632
this._ending = true
631633

632634
// if we have never connected, then end is a noop, callback immediately
633-
if (!this.connection._connecting) {
635+
if (!this.connection._connecting || this._ended) {
634636
if (cb) {
635637
cb()
636638
} else {

packages/pg/lib/connection.js

-3
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,6 @@ class Connection extends EventEmitter {
108108
}
109109

110110
attachListeners(stream) {
111-
stream.on('end', () => {
112-
this.emit('end')
113-
})
114111
parse(stream, (msg) => {
115112
var eventName = msg.name === 'error' ? 'errorMessage' : msg.name
116113
if (this._emitMessage) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
'use strict'
2+
const helper = require('../test-helper')
3+
4+
const suite = new helper.Suite()
5+
6+
// https://github.com/brianc/node-postgres/issues/2716
7+
suite.testAsync('client.end() should resolve if already ended', async () => {
8+
const client = new helper.pg.Client()
9+
await client.connect()
10+
11+
// this should resolve only when the underlying socket is fully closed, both
12+
// the readable part ("end" event) & writable part ("close" event).
13+
14+
// https://nodejs.org/docs/latest-v16.x/api/net.html#event-end
15+
// > Emitted when the other end of the socket signals the end of
16+
// > transmission, thus ending the readable side of the socket.
17+
18+
// https://nodejs.org/docs/latest-v16.x/api/net.html#event-close_1
19+
// > Emitted once the socket is fully closed.
20+
21+
// here: stream = socket
22+
23+
await client.end()
24+
// connection.end()
25+
// stream.end()
26+
// ...
27+
// stream emits "end"
28+
// not listening to this event anymore so the promise doesn't resolve yet
29+
// stream emits "close"; no more events will be emitted from the stream
30+
// connection emits "end"
31+
// promise resolved
32+
33+
// This should now resolve immediately, rather than wait for connection.on('end')
34+
await client.end()
35+
36+
// this should resolve immediately, rather than waiting forever
37+
await client.end()
38+
})

0 commit comments

Comments
 (0)