Skip to content

Commit 5971813

Browse files
authored
fix(pg-cloudflare): prevent Client.end() from hanging (#3752)
* fix(pg-cloudflare): safely end closed sockets * fix(pg-cloudflare): complete socket shutdown handling * test(pg-cloudflare): retain write callback coverage
1 parent 4589038 commit 5971813

2 files changed

Lines changed: 43 additions & 11 deletions

File tree

packages/pg-cloudflare/src/index.ts

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,16 @@ export class CloudflareSocket extends EventEmitter {
111111
end(data = Buffer.alloc(0), encoding: BufferEncoding = 'utf8', callback: (...args: unknown[]) => void = () => {}) {
112112
log('ending CF socket')
113113
this.write(data, encoding, (err) => {
114-
this._cfSocket?.close()
114+
const socket = this._cfSocket
115+
const closePromise = socket?.close()
116+
closePromise
117+
?.then(() => {
118+
if (this._cfSocket === socket) {
119+
this._cfSocket = null
120+
this.emit('close')
121+
}
122+
})
123+
.catch((e) => this.emit('error', e))
115124
if (callback) callback(err)
116125
})
117126
return this
@@ -140,16 +149,21 @@ export class CloudflareSocket extends EventEmitter {
140149
}
141150

142151
_addClosedHandler() {
143-
this._cfSocket!.closed.then(() => {
144-
if (!this._upgrading) {
145-
log('CF socket closed')
146-
this._cfSocket = null
147-
this.emit('close')
148-
} else {
149-
this._upgrading = false
150-
this._upgraded = true
151-
}
152-
}).catch((e) => this.emit('error', e))
152+
const socket = this._cfSocket!
153+
socket.closed
154+
.then(() => {
155+
if (!this._upgrading) {
156+
log('CF socket closed')
157+
if (this._cfSocket === socket) {
158+
this._cfSocket = null
159+
this.emit('close')
160+
}
161+
} else {
162+
this._upgrading = false
163+
this._upgraded = true
164+
}
165+
})
166+
.catch((e) => this.emit('error', e))
153167
}
154168
}
155169

packages/pg-esm-test/pg-cloudflare.test.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,22 @@ describe('pg-cloudflare', () => {
3737

3838
await promise
3939
})
40+
41+
it('should emit close when ending a socket whose closed promise never settles', async () => {
42+
const socket = new CloudflareSocket()
43+
socket._cfSocket = {
44+
closed: new Promise(() => {}),
45+
close: async () => {},
46+
}
47+
socket._addClosedHandler()
48+
49+
await new Promise((resolve, reject) => {
50+
const timer = setTimeout(() => reject(new Error('close event was not emitted')), 100)
51+
socket.once('close', () => {
52+
clearTimeout(timer)
53+
resolve()
54+
})
55+
socket.end()
56+
})
57+
})
4058
})

0 commit comments

Comments
 (0)