Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 18 additions & 11 deletions packages/bolt-connection/src/channel/browser/browser-channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export default class WebSocketChannel {
this._receiveTimeout = null
this._receiveTimeoutStarted = false
this._receiveTimeoutId = null
this._closingPromise = null

const { scheme, error } = determineWebSocketScheme(config, protocolSupplier)
if (error) {
Expand Down Expand Up @@ -163,17 +164,23 @@ export default class WebSocketChannel {
* @returns {Promise} A promise that will be resolved after channel is closed
*/
close () {
return new Promise((resolve, reject) => {
this._clearConnectionTimeout()
if (this._ws && this._ws.readyState !== WS_CLOSED) {
this._open = false
this.stopReceiveTimeout()
this._ws.onclose = () => resolve()
this._ws.close()
} else {
resolve()
}
})
if (this._closingPromise === null) {
this._closingPromise = new Promise((resolve, reject) => {
this._clearConnectionTimeout()
if (this._ws && this._ws.readyState !== WS_CLOSED) {
this._open = false
this.stopReceiveTimeout()
this._ws.onclose = () => {
resolve()
}
this._ws.close()
} else {
resolve()
}
})
}

return this._closingPromise
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,41 @@ describe('WebSocketChannel', () => {
fakeSetTimeout.uninstall()
}
})

it('should return always the same promise', async () => {
const fakeSetTimeout = setTimeoutMock.install()
try {
// do not execute setTimeout callbacks
fakeSetTimeout.pause()
const address = ServerAddress.fromUrl('bolt://localhost:8989')
const driverConfig = { connectionTimeout: 4242 }
const channelConfig = new ChannelConfig(
address,
driverConfig,
SERVICE_UNAVAILABLE
)
webSocketChannel = new WebSocketChannel(
channelConfig,
undefined,
createWebSocketFactory(WS_OPEN)
)

const promise1 = webSocketChannel.close()
const promise2 = webSocketChannel.close()

expect(promise1).toBe(promise2)

await Promise.all([promise1, promise2])

const promise3 = webSocketChannel.close()

expect(promise3).toBe(promise2)

await promise3
} finally {
fakeSetTimeout.uninstall()
}
})
})

describe('.setupReceiveTimeout()', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export default class WebSocketChannel {
this._receiveTimeout = null
this._receiveTimeoutStarted = false
this._receiveTimeoutId = null
this._closingPromise = null

const { scheme, error } = determineWebSocketScheme(config, protocolSupplier)
if (error) {
Expand Down Expand Up @@ -163,17 +164,23 @@ export default class WebSocketChannel {
* @returns {Promise} A promise that will be resolved after channel is closed
*/
close () {
return new Promise((resolve, reject) => {
this._clearConnectionTimeout()
if (this._ws && this._ws.readyState !== WS_CLOSED) {
this._open = false
this.stopReceiveTimeout()
this._ws.onclose = () => resolve()
this._ws.close()
} else {
resolve()
}
})
if (this._closingPromise === null) {
this._closingPromise = new Promise((resolve, reject) => {
this._clearConnectionTimeout()
if (this._ws && this._ws.readyState !== WS_CLOSED) {
this._open = false
this.stopReceiveTimeout()
this._ws.onclose = () => {
resolve()
}
this._ws.close()
} else {
resolve()
}
})
}

return this._closingPromise
}

/**
Expand Down