From c35400c6f8457687665ab6253bb23f9ddbeb75ed Mon Sep 17 00:00:00 2001 From: Deepak Ganesh Date: Sun, 26 Jul 2026 21:27:03 +0530 Subject: [PATCH] fix(pg-cloudflare): safely end closed sockets Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- packages/pg-cloudflare/package.json | 5 +- packages/pg-cloudflare/src/index.test.ts | 62 ++++++++++++++++++++++++ packages/pg-cloudflare/src/index.ts | 2 +- packages/pg-cloudflare/tsconfig.json | 2 +- 4 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 packages/pg-cloudflare/src/index.test.ts diff --git a/packages/pg-cloudflare/package.json b/packages/pg-cloudflare/package.json index 4bc706c8a..5a63b69e1 100644 --- a/packages/pg-cloudflare/package.json +++ b/packages/pg-cloudflare/package.json @@ -6,6 +6,8 @@ "types": "dist/index.d.ts", "license": "MIT", "devDependencies": { + "@types/mocha": "^10.0.10", + "mocha": "^11.7.5", "ts-node": "^8.5.4", "typescript": "^6.0.3" }, @@ -23,7 +25,8 @@ "build": "tsc", "build:watch": "tsc --watch", "prepublish": "yarn build", - "test": "echo e2e test in pg package" + "pretest": "yarn build", + "test": "mocha dist/**/*.test.js" }, "repository": { "type": "git", diff --git a/packages/pg-cloudflare/src/index.test.ts b/packages/pg-cloudflare/src/index.test.ts new file mode 100644 index 000000000..ca11db92a --- /dev/null +++ b/packages/pg-cloudflare/src/index.test.ts @@ -0,0 +1,62 @@ +import assert from 'assert' + +import { CloudflareSocket } from './index' + +function deferred() { + let resolve!: () => void + const promise = new Promise((done) => { + resolve = done + }) + return { promise, resolve } +} + +describe('CloudflareSocket', () => { + it('closes an open socket and invokes the end callback', () => { + const socket = new CloudflareSocket(false) + let closeCalls = 0 + Object.assign(socket, { + _cfSocket: { + close: () => closeCalls++, + }, + }) + + let callbackCalls = 0 + socket.end(Buffer.alloc(0), 'utf8', (err) => { + callbackCalls++ + assert.equal(err, undefined) + }) + + assert.equal(closeCalls, 1) + assert.equal(callbackCalls, 1) + }) + + it('invokes the end callback after the closed handler clears the socket', async () => { + const socket = new CloudflareSocket(false) + const closed = deferred() + let closeCalls = 0 + Object.assign(socket, { + _cfSocket: { + closed: closed.promise, + close: () => closeCalls++, + }, + }) + + let closeEvents = 0 + socket.on('close', () => closeEvents++) + socket._addClosedHandler() + closed.resolve() + await closed.promise + + let callbackCalls = 0 + assert.doesNotThrow(() => { + socket.end(Buffer.alloc(0), 'utf8', (err) => { + callbackCalls++ + assert.equal(err, undefined) + }) + }) + + assert.equal(closeCalls, 0) + assert.equal(closeEvents, 1) + assert.equal(callbackCalls, 1) + }) +}) diff --git a/packages/pg-cloudflare/src/index.ts b/packages/pg-cloudflare/src/index.ts index 9b1e517ba..357131911 100644 --- a/packages/pg-cloudflare/src/index.ts +++ b/packages/pg-cloudflare/src/index.ts @@ -107,7 +107,7 @@ export class CloudflareSocket extends EventEmitter { end(data = Buffer.alloc(0), encoding: BufferEncoding = 'utf8', callback: (...args: unknown[]) => void = () => {}) { log('ending CF socket') this.write(data, encoding, (err) => { - this._cfSocket!.close() + this._cfSocket?.close() if (callback) callback(err) }) return this diff --git a/packages/pg-cloudflare/tsconfig.json b/packages/pg-cloudflare/tsconfig.json index 840b52aff..7de8f3887 100644 --- a/packages/pg-cloudflare/tsconfig.json +++ b/packages/pg-cloudflare/tsconfig.json @@ -18,7 +18,7 @@ "./src/types/*" ] }, - "types": ["node"] + "types": ["node", "mocha"] }, "include": [ "src/**/*"