diff --git a/packages/pg-cloudflare/src/index.ts b/packages/pg-cloudflare/src/index.ts index 527b433f5..366752d1e 100644 --- a/packages/pg-cloudflare/src/index.ts +++ b/packages/pg-cloudflare/src/index.ts @@ -163,7 +163,15 @@ export class CloudflareSocket extends EventEmitter { this._upgraded = true } }) - .catch((e) => this.emit('error', e)) + .catch((e) => { + // workerd can reject `closed` with `undefined` + this.emit('error', e ?? new Error('Socket closed with an error')) + // like net.Socket, follow the error with 'close' so the connection ends + if (this._cfSocket === socket) { + this._cfSocket = null + this.emit('close') + } + }) } } diff --git a/packages/pg-esm-test/pg-cloudflare.test.js b/packages/pg-esm-test/pg-cloudflare.test.js index 683229158..6c53f50e7 100644 --- a/packages/pg-esm-test/pg-cloudflare.test.js +++ b/packages/pg-esm-test/pg-cloudflare.test.js @@ -19,6 +19,29 @@ describe('pg-cloudflare', () => { assert.doesNotThrow(() => socket.end()) }) + it('should emit error then close when the closed promise rejects', async () => { + const socket = new CloudflareSocket() + const underlyingSocket = { closed: Promise.reject(undefined) } + socket._cfSocket = underlyingSocket + + const events = [] + socket.on('error', (error) => events.push(error)) + await new Promise((resolve, reject) => { + const timer = setTimeout(() => reject(new Error('close event was not emitted')), 100) + socket.once('close', () => { + clearTimeout(timer) + events.push('close') + resolve() + }) + socket._addClosedHandler() + }) + + assert.equal(events.length, 2) + assert.ok(events[0] instanceof Error) + assert.equal(events[1], 'close') + assert.equal(socket._cfSocket, null) + }) + it('should call the write(data, callback) callback exactly once', async () => { const socket = new CloudflareSocket() socket._cfWriter = { write: () => Promise.resolve() }