@@ -28771,7 +28771,12 @@ var USER_AGENT = "neo4j-javascript/" + _version2.default;
28771
28771
* // {@link Session#readTransaction()} and {@link Session#writeTransaction()} functions. These functions
28772
28772
* // will retry the given unit of work on `ServiceUnavailable`, `SessionExpired` and transient errors with
28773
28773
* // exponential backoff using initial delay of 1 second. Default value is 30000 which is 30 seconds.
28774
- * maxTransactionRetryTime: 30000,
28774
+ * maxTransactionRetryTime: 30000, // 30 seconds
28775
+ *
28776
+ * // Specify socket connection timeout in milliseconds. Non-numeric, negative and zero values are treated as an
28777
+ * // infinite timeout. Connection will be then bound by the timeout configured on the operating system level.
28778
+ * // Timeout value should be numeric and greater or equal to zero.
28779
+ * connectionTimeout: 5000, // 5 seconds
28775
28780
* }
28776
28781
*
28777
28782
* @param {string} url The URL for the Neo4j database, for instance "bolt://localhost"
@@ -30808,10 +30813,6 @@ var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
30808
30813
30809
30814
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
30810
30815
30811
- var _createClass2 = require('babel-runtime/helpers/createClass');
30812
-
30813
- var _createClass3 = _interopRequireDefault(_createClass2);
30814
-
30815
30816
var _features = require('./features');
30816
30817
30817
30818
var _features2 = _interopRequireDefault(_features);
@@ -30839,54 +30840,56 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
30839
30840
* limitations under the License.
30840
30841
*/
30841
30842
30842
- var ChannelConfig = function () {
30843
- function ChannelConfig(host, port, driverConfig, connectionErrorCode) {
30844
- (0, _classCallCheck3.default)(this, ChannelConfig);
30843
+ var DEFAULT_CONNECTION_TIMEOUT_MILLIS = 0; // turned off by default
30845
30844
30846
- this.host = host;
30847
- this.port = port;
30848
- this.encrypted = ChannelConfig._extractEncrypted(driverConfig);
30849
- this.trust = ChannelConfig._extractTrust(driverConfig);
30850
- this.trustedCertificates = ChannelConfig._extractTrustedCertificates(driverConfig);
30851
- this.knownHostsPath = ChannelConfig._extractKnownHostsPath(driverConfig);
30852
- this.connectionErrorCode = connectionErrorCode || _error.SERVICE_UNAVAILABLE;
30853
- }
30845
+ var ChannelConfig = function ChannelConfig(host, port, driverConfig, connectionErrorCode) {
30846
+ (0, _classCallCheck3.default)(this, ChannelConfig);
30854
30847
30855
- (0, _createClass3.default)(ChannelConfig, null, [{
30856
- key: '_extractEncrypted',
30857
- value: function _extractEncrypted(driverConfig) {
30858
- // check if encryption was configured by the user, use explicit null check because we permit boolean value
30859
- var encryptionConfigured = driverConfig.encrypted == null;
30860
- // default to using encryption if trust-all-certificates is available
30861
- return encryptionConfigured ? (0, _features2.default)('trust_all_certificates') : driverConfig.encrypted;
30862
- }
30863
- }, {
30864
- key: '_extractTrust',
30865
- value: function _extractTrust(driverConfig) {
30866
- if (driverConfig.trust) {
30867
- return driverConfig.trust;
30868
- }
30869
- // default to using TRUST_ALL_CERTIFICATES if it is available
30870
- return (0, _features2.default)('trust_all_certificates') ? 'TRUST_ALL_CERTIFICATES' : 'TRUST_CUSTOM_CA_SIGNED_CERTIFICATES';
30871
- }
30872
- }, {
30873
- key: '_extractTrustedCertificates',
30874
- value: function _extractTrustedCertificates(driverConfig) {
30875
- return driverConfig.trustedCertificates || [];
30876
- }
30877
- }, {
30878
- key: '_extractKnownHostsPath',
30879
- value: function _extractKnownHostsPath(driverConfig) {
30880
- return driverConfig.knownHosts || null;
30881
- }
30882
- }]);
30883
- return ChannelConfig;
30884
- }();
30848
+ this.host = host;
30849
+ this.port = port;
30850
+ this.encrypted = extractEncrypted(driverConfig);
30851
+ this.trust = extractTrust(driverConfig);
30852
+ this.trustedCertificates = extractTrustedCertificates(driverConfig);
30853
+ this.knownHostsPath = extractKnownHostsPath(driverConfig);
30854
+ this.connectionErrorCode = connectionErrorCode || _error.SERVICE_UNAVAILABLE;
30855
+ this.connectionTimeout = extractConnectionTimeout(driverConfig);
30856
+ };
30885
30857
30886
30858
exports.default = ChannelConfig;
30887
- ;
30888
30859
30889
- },{"../error":292,"./features":305,"babel-runtime/helpers/classCallCheck":28,"babel-runtime/helpers/createClass":29}],299:[function(require,module,exports){
30860
+
30861
+ function extractEncrypted(driverConfig) {
30862
+ // check if encryption was configured by the user, use explicit null check because we permit boolean value
30863
+ var encryptionConfigured = driverConfig.encrypted == null;
30864
+ // default to using encryption if trust-all-certificates is available
30865
+ return encryptionConfigured ? (0, _features2.default)('trust_all_certificates') : driverConfig.encrypted;
30866
+ }
30867
+
30868
+ function extractTrust(driverConfig) {
30869
+ if (driverConfig.trust) {
30870
+ return driverConfig.trust;
30871
+ }
30872
+ // default to using TRUST_ALL_CERTIFICATES if it is available
30873
+ return (0, _features2.default)('trust_all_certificates') ? 'TRUST_ALL_CERTIFICATES' : 'TRUST_CUSTOM_CA_SIGNED_CERTIFICATES';
30874
+ }
30875
+
30876
+ function extractTrustedCertificates(driverConfig) {
30877
+ return driverConfig.trustedCertificates || [];
30878
+ }
30879
+
30880
+ function extractKnownHostsPath(driverConfig) {
30881
+ return driverConfig.knownHosts || null;
30882
+ }
30883
+
30884
+ function extractConnectionTimeout(driverConfig) {
30885
+ var configuredTimeout = parseInt(driverConfig.connectionTimeout, 10);
30886
+ if (!configuredTimeout || configuredTimeout < 0) {
30887
+ return DEFAULT_CONNECTION_TIMEOUT_MILLIS;
30888
+ }
30889
+ return configuredTimeout;
30890
+ }
30891
+
30892
+ },{"../error":292,"./features":305,"babel-runtime/helpers/classCallCheck":28}],299:[function(require,module,exports){
30890
30893
'use strict';
30891
30894
30892
30895
Object.defineProperty(exports, "__esModule", {
@@ -31208,6 +31211,8 @@ var NodeChannel = function () {
31208
31211
self.write(pending[i]);
31209
31212
}
31210
31213
}, this._handleConnectionError);
31214
+
31215
+ this._setupConnectionTimeout(config, this._conn);
31211
31216
}
31212
31217
31213
31218
(0, _createClass3.default)(NodeChannel, [{
@@ -31227,6 +31232,33 @@ var NodeChannel = function () {
31227
31232
this.onerror(this._error);
31228
31233
}
31229
31234
}
31235
+
31236
+ /**
31237
+ * Setup connection timeout on the socket, if configured.
31238
+ * @param {ChannelConfig} config - configuration of this channel.
31239
+ * @param {object} socket - `net.Socket` or `tls.TLSSocket` object.
31240
+ * @private
31241
+ */
31242
+
31243
+ }, {
31244
+ key: '_setupConnectionTimeout',
31245
+ value: function _setupConnectionTimeout(config, socket) {
31246
+ var timeout = config.connectionTimeout;
31247
+ if (timeout) {
31248
+ socket.on('connect', function () {
31249
+ // connected - clear connection timeout
31250
+ socket.setTimeout(0);
31251
+ });
31252
+
31253
+ socket.on('timeout', function () {
31254
+ // timeout fired - not connected within configured time. cancel timeout and destroy socket
31255
+ socket.setTimeout(0);
31256
+ socket.destroy((0, _error.newError)('Failed to establish connection in ' + timeout + 'ms', config.connectionErrorCode));
31257
+ });
31258
+
31259
+ socket.setTimeout(timeout);
31260
+ }
31261
+ }
31230
31262
}, {
31231
31263
key: 'isEncrypted',
31232
31264
value: function isEncrypted() {
@@ -31330,9 +31362,7 @@ var WebSocketChannel = function () {
31330
31362
this._pending = [];
31331
31363
this._error = null;
31332
31364
this._handleConnectionError = this._handleConnectionError.bind(this);
31333
- this._connectionErrorCode = config.connectionErrorCode;
31334
-
31335
- this._encrypted = config.encrypted;
31365
+ this._config = config;
31336
31366
31337
31367
var scheme = "ws";
31338
31368
//Allow boolean for backwards compatibility
@@ -31357,6 +31387,9 @@ var WebSocketChannel = function () {
31357
31387
}
31358
31388
};
31359
31389
this._ws.onopen = function () {
31390
+ // Connected! Cancel connection timeout
31391
+ clearTimeout(self._connectionTimeoutId);
31392
+
31360
31393
// Drain all pending messages
31361
31394
var pending = self._pending;
31362
31395
self._pending = null;
@@ -31372,15 +31405,28 @@ var WebSocketChannel = function () {
31372
31405
};
31373
31406
31374
31407
this._ws.onerror = this._handleConnectionError;
31408
+
31409
+ this._connectionTimeoutFired = false;
31410
+ this._connectionTimeoutId = this._setupConnectionTimeout(config);
31375
31411
}
31376
31412
31377
31413
(0, _createClass3.default)(WebSocketChannel, [{
31378
31414
key: '_handleConnectionError',
31379
31415
value: function _handleConnectionError() {
31416
+ if (this._connectionTimeoutFired) {
31417
+ // timeout fired - not connected within configured time
31418
+ this._error = (0, _error.newError)('Failed to establish connection in ' + this._config.connectionTimeout + 'ms', this._config.connectionErrorCode);
31419
+
31420
+ if (this.onerror) {
31421
+ this.onerror(this._error);
31422
+ }
31423
+ return;
31424
+ }
31425
+
31380
31426
// onerror triggers on websocket close as well.. don't get me started.
31381
31427
if (this._open) {
31382
31428
// http://stackoverflow.com/questions/25779831/how-to-catch-websocket-connection-to-ws-xxxnn-failed-connection-closed-be
31383
- this._error = (0, _error.newError)("WebSocket connection failure. Due to security " + "constraints in your web browser, the reason for the failure is not available " + "to this Neo4j Driver. Please use your browsers development console to determine " + "the root cause of the failure. Common reasons include the database being " + "unavailable, using the wrong connection URL or temporary network problems. " + "If you have enabled encryption, ensure your browser is configured to trust the " + " certificate Neo4j is configured to use. WebSocket `readyState` is: " + this._ws.readyState, this._connectionErrorCode );
31429
+ this._error = (0, _error.newError)("WebSocket connection failure. Due to security " + "constraints in your web browser, the reason for the failure is not available " + "to this Neo4j Driver. Please use your browsers development console to determine " + "the root cause of the failure. Common reasons include the database being " + "unavailable, using the wrong connection URL or temporary network problems. " + "If you have enabled encryption, ensure your browser is configured to trust the " + ' certificate Neo4j is configured to use. WebSocket `readyState` is: ' + this._ws.readyState, this._config.connectionErrorCode );
31384
31430
if (this.onerror) {
31385
31431
this.onerror(this._error);
31386
31432
}
@@ -31389,7 +31435,7 @@ var WebSocketChannel = function () {
31389
31435
}, {
31390
31436
key: 'isEncrypted',
31391
31437
value: function isEncrypted() {
31392
- return this._encrypted ;
31438
+ return this._config.encrypted ;
31393
31439
}
31394
31440
31395
31441
/**
@@ -31427,6 +31473,31 @@ var WebSocketChannel = function () {
31427
31473
this._ws.close();
31428
31474
this._ws.onclose = cb;
31429
31475
}
31476
+
31477
+ /**
31478
+ * Set connection timeout on the given WebSocket, if configured.
31479
+ * @return {number} the timeout id or null.
31480
+ * @private
31481
+ */
31482
+
31483
+ }, {
31484
+ key: '_setupConnectionTimeout',
31485
+ value: function _setupConnectionTimeout() {
31486
+ var _this = this;
31487
+
31488
+ var timeout = this._config.connectionTimeout;
31489
+ if (timeout) {
31490
+ var webSocket = this._ws;
31491
+
31492
+ return setTimeout(function () {
31493
+ if (webSocket.readyState !== WebSocket.OPEN) {
31494
+ _this._connectionTimeoutFired = true;
31495
+ webSocket.close();
31496
+ }
31497
+ }, timeout);
31498
+ }
31499
+ return null;
31500
+ }
31430
31501
}]);
31431
31502
return WebSocketChannel;
31432
31503
}(); /**
@@ -33875,6 +33946,10 @@ Object.defineProperty(exports, "__esModule", {
33875
33946
value: true
33876
33947
});
33877
33948
33949
+ var _keys = require("babel-runtime/core-js/object/keys");
33950
+
33951
+ var _keys2 = _interopRequireDefault(_keys);
33952
+
33878
33953
var _classCallCheck2 = require("babel-runtime/helpers/classCallCheck");
33879
33954
33880
33955
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
@@ -33970,11 +34045,11 @@ var Pool = function () {
33970
34045
}, {
33971
34046
key: "purgeAll",
33972
34047
value: function purgeAll() {
33973
- for ( var key in this._pools.keys) {
33974
- if (this._pools.hasOwnPropertykey) {
33975
- this.purge( key);
33976
- }
33977
- }
34048
+ var _this = this;
34049
+
34050
+ (0, _keys2.default)( this._pools).forEach(function ( key) {
34051
+ return _this.purge(key);
34052
+ });
33978
34053
}
33979
34054
}, {
33980
34055
key: "has",
@@ -33986,7 +34061,8 @@ var Pool = function () {
33986
34061
value: function _release(key, resource) {
33987
34062
var pool = this._pools[key];
33988
34063
if (!pool) {
33989
- //key has been purged, don't put it back
34064
+ // key has been purged, don't put it back, just destroy the resource
34065
+ this._destroy(resource);
33990
34066
return;
33991
34067
}
33992
34068
if (pool.length >= this._maxIdle || !this._validate(resource)) {
@@ -34001,7 +34077,7 @@ var Pool = function () {
34001
34077
34002
34078
exports.default = Pool;
34003
34079
34004
- },{"babel-runtime/helpers/classCallCheck":28,"babel-runtime/helpers/createClass":29}],309:[function(require,module,exports){
34080
+ },{"babel-runtime/core-js/object/keys":22,"babel-runtime/ helpers/classCallCheck":28,"babel-runtime/helpers/createClass":29}],309:[function(require,module,exports){
34005
34081
'use strict';
34006
34082
34007
34083
Object.defineProperty(exports, "__esModule", {
@@ -35161,7 +35237,7 @@ exports.default = platformObj;
35161
35237
Object.defineProperty(exports, "__esModule", {
35162
35238
value: true
35163
35239
});
35164
- exports.ENCRYPTION_OFF = exports.ENCRYPTION_ON = exports.parseRoutingContext = exports.parsePort = exports.parseHost = exports.parseUrl = exports.parseScheme = exports.assertString = exports.isString = exports.isEmptyObjectOrNull = undefined;
35240
+ exports.ENCRYPTION_OFF = exports.ENCRYPTION_ON = exports.parseRoutingContext = exports.parsePort = exports.parseHost = exports.parseUrl = exports.parseScheme = exports.assertCypherStatement = exports. assertString = exports.isString = exports.isEmptyObjectOrNull = undefined;
35165
35241
35166
35242
var _stringify = require("babel-runtime/core-js/json/stringify");
35167
35243
@@ -35232,6 +35308,14 @@ function assertString(obj, objName) {
35232
35308
return obj;
35233
35309
}
35234
35310
35311
+ function assertCypherStatement(obj) {
35312
+ assertString(obj, 'Cypher statement');
35313
+ if (obj.trim().length == 0) {
35314
+ throw new TypeError('Cypher statement is expected to be a non-empty string.');
35315
+ }
35316
+ return obj;
35317
+ }
35318
+
35235
35319
function isString(str) {
35236
35320
return Object.prototype.toString.call(str) === '[object String]';
35237
35321
}
@@ -35291,6 +35375,7 @@ function trimAndVerify(string, name, url) {
35291
35375
exports.isEmptyObjectOrNull = isEmptyObjectOrNull;
35292
35376
exports.isString = isString;
35293
35377
exports.assertString = assertString;
35378
+ exports.assertCypherStatement = assertCypherStatement;
35294
35379
exports.parseScheme = parseScheme;
35295
35380
exports.parseUrl = parseUrl;
35296
35381
exports.parseHost = parseHost;
@@ -36294,7 +36379,7 @@ var Session = function () {
36294
36379
36295
36380
/**
36296
36381
* Run Cypher statement
36297
- * Could be called with a statement object i.e.: {statement : "MATCH ...", parameters: {param: 1}}
36382
+ * Could be called with a statement object i.e.: {text : "MATCH ...", parameters: {param: 1}}
36298
36383
* or with the statement and parameters as separate arguments.
36299
36384
* @param {mixed} statement - Cypher statement to execute
36300
36385
* @param {Object} parameters - Map with parameters to use in statement
@@ -36311,7 +36396,7 @@ var Session = function () {
36311
36396
parameters = statement.parameters || {};
36312
36397
statement = statement.text;
36313
36398
}
36314
- (0, _util.assertString )(statement, 'Cypher statement' );
36399
+ (0, _util.assertCypherStatement )(statement);
36315
36400
36316
36401
return this._run(statement, parameters, function (connection, streamObserver) {
36317
36402
return connection.run(statement, parameters, streamObserver);
@@ -36620,7 +36705,7 @@ var Transaction = function () {
36620
36705
36621
36706
/**
36622
36707
* Run Cypher statement
36623
- * Could be called with a statement object i.e.: <code>{statement : "MATCH ...", parameters: {param: 1}}</code>
36708
+ * Could be called with a statement object i.e.: <code>{text : "MATCH ...", parameters: {param: 1}}</code>
36624
36709
* or with the statement and parameters as separate arguments.
36625
36710
* @param {mixed} statement - Cypher statement to execute
36626
36711
* @param {Object} parameters - Map with parameters to use in statement
@@ -36635,7 +36720,7 @@ var Transaction = function () {
36635
36720
parameters = statement.parameters || {};
36636
36721
statement = statement.text;
36637
36722
}
36638
- (0, _util.assertString )(statement, "Cypher statement" );
36723
+ (0, _util.assertCypherStatement )(statement);
36639
36724
36640
36725
return this._state.run(this._connectionHolder, new _TransactionStreamObserver(this), statement, parameters);
36641
36726
}
0 commit comments