@@ -28771,7 +28771,12 @@ var USER_AGENT = "neo4j-javascript/" + _version2.default;
2877128771 * // {@link Session#readTransaction()} and {@link Session#writeTransaction()} functions. These functions
2877228772 * // will retry the given unit of work on `ServiceUnavailable`, `SessionExpired` and transient errors with
2877328773 * // 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
2877528780 * }
2877628781 *
2877728782 * @param {string} url The URL for the Neo4j database, for instance "bolt://localhost"
@@ -30808,10 +30813,6 @@ var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
3080830813
3080930814var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
3081030815
30811- var _createClass2 = require('babel-runtime/helpers/createClass');
30812-
30813- var _createClass3 = _interopRequireDefault(_createClass2);
30814-
3081530816var _features = require('./features');
3081630817
3081730818var _features2 = _interopRequireDefault(_features);
@@ -30839,54 +30840,56 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
3083930840 * limitations under the License.
3084030841 */
3084130842
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
3084530844
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);
3085430847
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+ };
3088530857
3088630858exports.default = ChannelConfig;
30887- ;
3088830859
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){
3089030893'use strict';
3089130894
3089230895Object.defineProperty(exports, "__esModule", {
@@ -31208,6 +31211,8 @@ var NodeChannel = function () {
3120831211 self.write(pending[i]);
3120931212 }
3121031213 }, this._handleConnectionError);
31214+
31215+ this._setupConnectionTimeout(config, this._conn);
3121131216 }
3121231217
3121331218 (0, _createClass3.default)(NodeChannel, [{
@@ -31227,6 +31232,33 @@ var NodeChannel = function () {
3122731232 this.onerror(this._error);
3122831233 }
3122931234 }
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+ }
3123031262 }, {
3123131263 key: 'isEncrypted',
3123231264 value: function isEncrypted() {
@@ -31330,9 +31362,7 @@ var WebSocketChannel = function () {
3133031362 this._pending = [];
3133131363 this._error = null;
3133231364 this._handleConnectionError = this._handleConnectionError.bind(this);
31333- this._connectionErrorCode = config.connectionErrorCode;
31334-
31335- this._encrypted = config.encrypted;
31365+ this._config = config;
3133631366
3133731367 var scheme = "ws";
3133831368 //Allow boolean for backwards compatibility
@@ -31357,6 +31387,9 @@ var WebSocketChannel = function () {
3135731387 }
3135831388 };
3135931389 this._ws.onopen = function () {
31390+ // Connected! Cancel connection timeout
31391+ clearTimeout(self._connectionTimeoutId);
31392+
3136031393 // Drain all pending messages
3136131394 var pending = self._pending;
3136231395 self._pending = null;
@@ -31372,15 +31405,28 @@ var WebSocketChannel = function () {
3137231405 };
3137331406
3137431407 this._ws.onerror = this._handleConnectionError;
31408+
31409+ this._connectionTimeoutFired = false;
31410+ this._connectionTimeoutId = this._setupConnectionTimeout(config);
3137531411 }
3137631412
3137731413 (0, _createClass3.default)(WebSocketChannel, [{
3137831414 key: '_handleConnectionError',
3137931415 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+
3138031426 // onerror triggers on websocket close as well.. don't get me started.
3138131427 if (this._open) {
3138231428 // 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 );
3138431430 if (this.onerror) {
3138531431 this.onerror(this._error);
3138631432 }
@@ -31389,7 +31435,7 @@ var WebSocketChannel = function () {
3138931435 }, {
3139031436 key: 'isEncrypted',
3139131437 value: function isEncrypted() {
31392- return this._encrypted ;
31438+ return this._config.encrypted ;
3139331439 }
3139431440
3139531441 /**
@@ -31427,6 +31473,31 @@ var WebSocketChannel = function () {
3142731473 this._ws.close();
3142831474 this._ws.onclose = cb;
3142931475 }
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+ }
3143031501 }]);
3143131502 return WebSocketChannel;
3143231503}(); /**
@@ -33875,6 +33946,10 @@ Object.defineProperty(exports, "__esModule", {
3387533946 value: true
3387633947});
3387733948
33949+ var _keys = require("babel-runtime/core-js/object/keys");
33950+
33951+ var _keys2 = _interopRequireDefault(_keys);
33952+
3387833953var _classCallCheck2 = require("babel-runtime/helpers/classCallCheck");
3387933954
3388033955var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
@@ -33970,11 +34045,11 @@ var Pool = function () {
3397034045 }, {
3397134046 key: "purgeAll",
3397234047 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+ });
3397834053 }
3397934054 }, {
3398034055 key: "has",
@@ -33986,7 +34061,8 @@ var Pool = function () {
3398634061 value: function _release(key, resource) {
3398734062 var pool = this._pools[key];
3398834063 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);
3399034066 return;
3399134067 }
3399234068 if (pool.length >= this._maxIdle || !this._validate(resource)) {
@@ -34001,7 +34077,7 @@ var Pool = function () {
3400134077
3400234078exports.default = Pool;
3400334079
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){
3400534081'use strict';
3400634082
3400734083Object.defineProperty(exports, "__esModule", {
@@ -35161,7 +35237,7 @@ exports.default = platformObj;
3516135237Object.defineProperty(exports, "__esModule", {
3516235238 value: true
3516335239});
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;
3516535241
3516635242var _stringify = require("babel-runtime/core-js/json/stringify");
3516735243
@@ -35232,6 +35308,14 @@ function assertString(obj, objName) {
3523235308 return obj;
3523335309}
3523435310
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+
3523535319function isString(str) {
3523635320 return Object.prototype.toString.call(str) === '[object String]';
3523735321}
@@ -35291,6 +35375,7 @@ function trimAndVerify(string, name, url) {
3529135375exports.isEmptyObjectOrNull = isEmptyObjectOrNull;
3529235376exports.isString = isString;
3529335377exports.assertString = assertString;
35378+ exports.assertCypherStatement = assertCypherStatement;
3529435379exports.parseScheme = parseScheme;
3529535380exports.parseUrl = parseUrl;
3529635381exports.parseHost = parseHost;
@@ -36294,7 +36379,7 @@ var Session = function () {
3629436379
3629536380 /**
3629636381 * 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}}
3629836383 * or with the statement and parameters as separate arguments.
3629936384 * @param {mixed} statement - Cypher statement to execute
3630036385 * @param {Object} parameters - Map with parameters to use in statement
@@ -36311,7 +36396,7 @@ var Session = function () {
3631136396 parameters = statement.parameters || {};
3631236397 statement = statement.text;
3631336398 }
36314- (0, _util.assertString )(statement, 'Cypher statement' );
36399+ (0, _util.assertCypherStatement )(statement);
3631536400
3631636401 return this._run(statement, parameters, function (connection, streamObserver) {
3631736402 return connection.run(statement, parameters, streamObserver);
@@ -36620,7 +36705,7 @@ var Transaction = function () {
3662036705
3662136706 /**
3662236707 * 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>
3662436709 * or with the statement and parameters as separate arguments.
3662536710 * @param {mixed} statement - Cypher statement to execute
3662636711 * @param {Object} parameters - Map with parameters to use in statement
@@ -36635,7 +36720,7 @@ var Transaction = function () {
3663536720 parameters = statement.parameters || {};
3663636721 statement = statement.text;
3663736722 }
36638- (0, _util.assertString )(statement, "Cypher statement" );
36723+ (0, _util.assertCypherStatement )(statement);
3663936724
3664036725 return this._state.run(this._connectionHolder, new _TransactionStreamObserver(this), statement, parameters);
3664136726 }
0 commit comments