Skip to content

Commit 07ec4cb

Browse files
committed
Modify driver.close() and related internals to return Promise
1 parent 00632b4 commit 07ec4cb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+2852
-3988
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ var neo4j = require('neo4j-driver')
4848
Driver instance should be closed when Node.js application exits:
4949

5050
```javascript
51-
driver.close()
51+
driver.close() // returns a Promise
5252
```
5353

5454
otherwise application shutdown might hang or it might exit with a non-zero exit code.
@@ -87,7 +87,7 @@ WebSockets when the page is unloaded. However, driver instance should be explici
8787
is not the same as the lifetime of the web page:
8888

8989
```javascript
90-
driver.close()
90+
driver.close() // returns a Promise
9191
```
9292

9393
## Usage examples
@@ -104,7 +104,7 @@ var driver = neo4j.driver(
104104

105105
// Close the driver when application exits.
106106
// This closes all used network connections.
107-
driver.close()
107+
await driver.close()
108108
```
109109

110110
### Acquiring a Session
@@ -189,7 +189,7 @@ session
189189
console.log(record.get('name'))
190190
},
191191
onCompleted: () => {
192-
session.close()
192+
session.close() // returns a Promise
193193
},
194194
onError: error => {
195195
console.log(error)

src/driver.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,9 @@ class Driver {
174174
close () {
175175
this._log.info(`Driver ${this._id} closing`)
176176
if (this._connectionProvider) {
177-
this._connectionProvider.close()
177+
return this._connectionProvider.close()
178178
}
179+
return Promise.resolve()
179180
}
180181

181182
/**

src/internal/browser/browser-channel.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@ export default class WebSocketChannel {
3838
* @param {ChannelConfig} config - configuration for this channel.
3939
* @param {function(): string} protocolSupplier - function that detects protocol of the web page. Should only be used in tests.
4040
*/
41-
constructor (config, protocolSupplier = detectWebPageProtocol) {
41+
constructor (
42+
config,
43+
protocolSupplier = detectWebPageProtocol,
44+
socketFactory = url => new WebSocket(url)
45+
) {
4246
this._open = true
4347
this._pending = []
4448
this._error = null
@@ -51,14 +55,14 @@ export default class WebSocketChannel {
5155
return
5256
}
5357

54-
this._ws = createWebSocket(scheme, config.address)
58+
this._ws = createWebSocket(scheme, config.address, socketFactory)
5559
this._ws.binaryType = 'arraybuffer'
5660

5761
let self = this
5862
// All connection errors are not sent to the error handler
5963
// we must also check for dirty close calls
6064
this._ws.onclose = function (e) {
61-
if (!e.wasClean) {
65+
if (e && !e.wasClean) {
6266
self._handleConnectionError()
6367
}
6468
}
@@ -142,7 +146,7 @@ export default class WebSocketChannel {
142146
*/
143147
close () {
144148
return new Promise((resolve, reject) => {
145-
if (this._ws && this._ws.readyState != WS_CLOSED) {
149+
if (this._ws && this._ws.readyState !== WS_CLOSED) {
146150
this._open = false
147151
this._clearConnectionTimeout()
148152
this._ws.onclose = () => resolve()
@@ -187,11 +191,11 @@ export default class WebSocketChannel {
187191
}
188192
}
189193

190-
function createWebSocket (scheme, address) {
194+
function createWebSocket (scheme, address, socketFactory) {
191195
const url = scheme + '://' + address.asHostPort()
192196

193197
try {
194-
return new WebSocket(url)
198+
return socketFactory(url)
195199
} catch (error) {
196200
if (isIPv6AddressIssueOnWindows(error, address)) {
197201
// WebSocket in IE and Edge browsers on Windows do not support regular IPv6 address syntax because they contain ':'.
@@ -208,7 +212,7 @@ function createWebSocket (scheme, address) {
208212
// That is why here we "catch" SyntaxError and rewrite IPv6 address if needed.
209213

210214
const windowsFriendlyUrl = asWindowsFriendlyIPv6Address(scheme, address)
211-
return new WebSocket(windowsFriendlyUrl)
215+
return socketFactory(windowsFriendlyUrl)
212216
} else {
213217
throw error
214218
}

src/internal/connection-channel.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ export default class ChannelConnection extends Connection {
440440
* Call close on the channel.
441441
* @returns {Promise<void>} - A promise that will be resolved when the underlying channel is closed.
442442
*/
443-
close () {
443+
async close () {
444444
if (this._log.isDebugEnabled()) {
445445
this._log.debug(`${this} closing`)
446446
}
@@ -451,11 +451,11 @@ export default class ChannelConnection extends Connection {
451451
this._protocol.prepareToClose()
452452
}
453453

454-
return this._ch.close().then(() => {
455-
if (this._log.isDebugEnabled()) {
456-
this._log.debug(`${this} closed`)
457-
}
458-
})
454+
await this._ch.close()
455+
456+
if (this._log.isDebugEnabled()) {
457+
this._log.debug(`${this} closed`)
458+
}
459459
}
460460

461461
toString () {

src/internal/connection-delegate.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,6 @@ export default class DelegateConnection extends Connection {
9292
this._delegate._errorHandler = this._originalErrorHandler
9393
}
9494

95-
this._delegate._release()
95+
return this._delegate._release()
9696
}
9797
}

src/internal/connection-provider-pooled.js

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -98,22 +98,16 @@ export default class PooledConnectionProvider extends ConnectionProvider {
9898
*/
9999
_destroyConnection (conn) {
100100
delete this._openConnections[conn.id]
101-
conn.close()
101+
return conn.close()
102102
}
103103

104-
close () {
105-
try {
106-
// purge all idle connections in the connection pool
107-
this._connectionPool.purgeAll()
108-
} finally {
109-
// then close all connections driver has ever created
110-
// it is needed to close connections that are active right now and are acquired from the pool
111-
for (let connectionId in this._openConnections) {
112-
if (this._openConnections.hasOwnProperty(connectionId)) {
113-
this._openConnections[connectionId].close()
114-
}
115-
}
116-
}
104+
async close () {
105+
// purge all idle connections in the connection pool
106+
await this._connectionPool.close()
107+
108+
// then close all connections driver has ever created
109+
// it is needed to close connections that are active right now and are acquired from the pool
110+
await Promise.all(Object.values(this._openConnections).map(c => c.close()))
117111
}
118112

119113
static _installIdleObserverOnConnection (conn, observer) {

src/internal/connection-provider-routing.js

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,9 @@ export default class RoutingConnectionProvider extends PooledConnectionProvider
155155
)
156156
}
157157

158-
this._connectionPool.purge(address)
158+
// We're firing and forgetting this operation explicitly and listening for any
159+
// errors to avoid unhandled promise rejection
160+
this._connectionPool.purge(address).catch(() => {})
159161
}
160162

161163
forgetWriter (address, database) {
@@ -223,10 +225,9 @@ export default class RoutingConnectionProvider extends PooledConnectionProvider
223225
currentRoutingTable
224226
)
225227
})
226-
.then(newRoutingTable => {
228+
.then(newRoutingTable =>
227229
this._applyRoutingTableIfPossible(currentRoutingTable, newRoutingTable)
228-
return newRoutingTable
229-
})
230+
)
230231
}
231232

232233
_fetchRoutingTableFromKnownRoutersFallbackToSeedRouter (
@@ -249,10 +250,9 @@ export default class RoutingConnectionProvider extends PooledConnectionProvider
249250
currentRoutingTable
250251
)
251252
})
252-
.then(newRoutingTable => {
253+
.then(newRoutingTable =>
253254
this._applyRoutingTableIfPossible(currentRoutingTable, newRoutingTable)
254-
return newRoutingTable
255-
})
255+
)
256256
}
257257

258258
_fetchRoutingTableUsingKnownRouters (knownRouters, currentRoutingTable) {
@@ -381,7 +381,7 @@ export default class RoutingConnectionProvider extends PooledConnectionProvider
381381
})
382382
}
383383

384-
_applyRoutingTableIfPossible (currentRoutingTable, newRoutingTable) {
384+
async _applyRoutingTableIfPossible (currentRoutingTable, newRoutingTable) {
385385
if (!newRoutingTable) {
386386
// none of routing servers returned valid routing table, throw exception
387387
throw newError(
@@ -396,12 +396,14 @@ export default class RoutingConnectionProvider extends PooledConnectionProvider
396396
this._useSeedRouter = true
397397
}
398398

399-
this._updateRoutingTable(newRoutingTable)
399+
await this._updateRoutingTable(newRoutingTable)
400+
401+
return newRoutingTable
400402
}
401403

402-
_updateRoutingTable (newRoutingTable) {
404+
async _updateRoutingTable (newRoutingTable) {
403405
// close old connections to servers not present in the new routing table
404-
this._connectionPool.keepAll(newRoutingTable.allServers())
406+
await this._connectionPool.keepAll(newRoutingTable.allServers())
405407

406408
// make this driver instance aware of the new table
407409
this._routingTables[newRoutingTable.database] = newRoutingTable

src/internal/node/node-channel.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ export default class NodeChannel {
278278
}
279279

280280
_handleConnectionTerminated () {
281+
this._open = false
281282
this._error = newError(
282283
'Connection was closed by server',
283284
this._connectionErrorCode
@@ -338,13 +339,23 @@ export default class NodeChannel {
338339
*/
339340
close () {
340341
return new Promise((resolve, reject) => {
342+
const cleanup = () => {
343+
if (!this._conn.destroyed) {
344+
this._conn.destroy()
345+
}
346+
347+
resolve()
348+
}
349+
341350
if (this._open) {
342351
this._open = false
343352
this._conn.removeListener('end', this._handleConnectionTerminated)
344-
this._conn.on('end', () => resolve())
353+
this._conn.on('end', () => cleanup())
354+
this._conn.on('close', () => cleanup())
345355
this._conn.end()
356+
this._conn.destroy()
346357
} else {
347-
resolve()
358+
cleanup()
348359
}
349360
})
350361
}

0 commit comments

Comments
 (0)