diff --git a/packages/pg-cloudflare/src/index.ts b/packages/pg-cloudflare/src/index.ts index 527b433f5..d675399d6 100644 --- a/packages/pg-cloudflare/src/index.ts +++ b/packages/pg-cloudflare/src/index.ts @@ -87,22 +87,24 @@ export class CloudflareSocket extends EventEmitter { write( data: Uint8Array | string, encodingOrCallback: BufferEncoding | ((error?: unknown) => void) = 'utf8', - callback: (error?: unknown) => void = () => {} + callback?: (error?: unknown) => void ): true | void { const encoding = typeof encodingOrCallback === 'function' ? 'utf8' : encodingOrCallback if (typeof encodingOrCallback === 'function') callback = encodingOrCallback - if (data.length === 0) return callback() + if (data.length === 0) return callback?.() if (typeof data === 'string') data = Buffer.from(data, encoding) log('sending data direct:', data) this._cfWriter!.write(data).then( () => { log('data sent') - callback() + callback?.() }, (err) => { log('send error', err) - callback(err) + // pg calls write() without a callback, so emit the error the way a net.Socket would + if (callback) callback(err) + else this.emit('error', err) } ) return true diff --git a/packages/pg-esm-test/pg-cloudflare.test.js b/packages/pg-esm-test/pg-cloudflare.test.js index 683229158..4a10d80b5 100644 --- a/packages/pg-esm-test/pg-cloudflare.test.js +++ b/packages/pg-esm-test/pg-cloudflare.test.js @@ -38,6 +38,37 @@ describe('pg-cloudflare', () => { await promise }) + it('should emit an error when a write without a callback fails', async () => { + const socket = new CloudflareSocket() + const writeError = new Error('write failed') + socket._cfWriter = { write: () => Promise.reject(writeError) } + + const error = await new Promise((resolve, reject) => { + const timer = setTimeout(() => reject(new Error('error event was not emitted')), 100) + socket.once('error', (err) => { + clearTimeout(timer) + resolve(err) + }) + socket.write(Buffer.from('x')) + }) + assert.equal(error, writeError) + }) + + it('should pass a failed write to the callback instead of emitting an error', async () => { + const socket = new CloudflareSocket() + const writeError = new Error('write failed') + socket._cfWriter = { write: () => Promise.reject(writeError) } + let emitted = false + socket.on('error', () => { + emitted = true + }) + + const error = await new Promise((resolve) => socket.write(Buffer.from('x'), resolve)) + assert.equal(error, writeError) + await new Promise((resolve) => setTimeout(resolve, 10)) + assert.equal(emitted, false) + }) + it('should emit close when ending a socket whose closed promise never settles', async () => { const socket = new CloudflareSocket() socket._cfSocket = {