Skip to content

Commit 6a4a353

Browse files
committed
Fix connection close by server treatment by websocket channels
The lack of set channel._open to false when the onclose event is triggered was causing the channel be broken without the other parts of the driver notice. In tbis way, a next iteration trying to get the broken connection will succeded in the try and run the query will result in a eternal pending promise. Mark the channel as closed enable the pool to discard and create a new connection if it needed.
1 parent ab0288f commit 6a4a353

File tree

2 files changed

+46
-2
lines changed

2 files changed

+46
-2
lines changed

src/v1/internal/browser/browser-channel.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -47,20 +47,21 @@ export default class WebSocketChannel {
4747
this._ws = createWebSocket(scheme, config.address)
4848
this._ws.binaryType = 'arraybuffer'
4949

50-
let self = this
50+
const self = this
5151
// All connection errors are not sent to the error handler
5252
// we must also check for dirty close calls
5353
this._ws.onclose = function (e) {
5454
if (!e.wasClean) {
5555
self._handleConnectionError()
5656
}
57+
self._open = false
5758
}
5859
this._ws.onopen = function () {
5960
// Connected! Cancel the connection timeout
6061
self._clearConnectionTimeout()
6162

6263
// Drain all pending messages
63-
let pending = self._pending
64+
const pending = self._pending
6465
self._pending = null
6566
for (let i = 0; i < pending.length; i++) {
6667
self.write(pending[i])

test/internal/browser/browser-channel.test.js

+43
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ import { setTimeoutMock } from '../timers-util'
2424
import { ENCRYPTION_OFF, ENCRYPTION_ON } from '../../../src/v1/internal/util'
2525
import ServerAddress from '../../../src/v1/internal/server-address'
2626

27+
const WS_OPEN = 1
28+
const WS_CLOSED = 3
29+
2730
/* eslint-disable no-global-assign */
2831
describe('WebSocketChannel', () => {
2932
let OriginalWebSocket
@@ -236,4 +239,44 @@ describe('WebSocketChannel', () => {
236239
expect(channel).toBeDefined()
237240
expect(warnMessages.length).toEqual(1)
238241
}
242+
243+
it('should set _open to false when connection closes', async () => {
244+
const fakeSetTimeout = setTimeoutMock.install()
245+
try {
246+
// do not execute setTimeout callbacks
247+
fakeSetTimeout.pause()
248+
const address = ServerAddress.fromUrl('bolt://localhost:8989')
249+
const driverConfig = { connectionTimeout: 4242 }
250+
const channelConfig = new ChannelConfig(
251+
address,
252+
driverConfig,
253+
SERVICE_UNAVAILABLE
254+
)
255+
webSocketChannel = new WebSocketChannel(
256+
channelConfig,
257+
undefined,
258+
createWebSocketFactory(WS_OPEN)
259+
)
260+
await webSocketChannel._ws.close()
261+
expect(webSocketChannel._ws.readyState).toBe(WS_CLOSED)
262+
expect(webSocketChannel._open).toBe(false)
263+
} finally {
264+
fakeSetTimeout.uninstall()
265+
}
266+
})
267+
268+
function createWebSocketFactory (readyState) {
269+
const ws = {}
270+
ws.readyState = readyState
271+
ws.close = () => {
272+
ws.readyState = WS_CLOSED
273+
if (ws.onclose && typeof ws.onclose === 'function') {
274+
ws.onclose({ wasClean: true })
275+
}
276+
}
277+
return url => {
278+
ws.url = url
279+
return ws
280+
}
281+
}
239282
})

0 commit comments

Comments
 (0)