From f5c19be0b423c32cb76e574f91cb27edd4a261f6 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 3 Feb 2014 11:21:38 -0500 Subject: [PATCH] Add option for connect timeout --- Changes.md | 2 ++ Readme.md | 2 ++ lib/Connection.js | 23 +++++++++++++++++++ lib/ConnectionConfig.js | 1 + .../connection/test-connect-timeout-only.js | 22 ++++++++++++++++++ .../connection/test-connect-timeout.js | 20 ++++++++++++++++ 6 files changed, 70 insertions(+) create mode 100644 test/integration/connection/test-connect-timeout-only.js create mode 100644 test/integration/connection/test-connect-timeout.js diff --git a/Changes.md b/Changes.md index cd7fe416f..d5a0c8ded 100644 --- a/Changes.md +++ b/Changes.md @@ -6,6 +6,8 @@ you spot any mistakes. ## HEAD +* Add `connectTimeout` option to specify a timeout for establishing a connection #726 + ## v2.0.1 * internal parser speed improvement #702 diff --git a/Readme.md b/Readme.md index 9e026d494..0a865a1b2 100644 --- a/Readme.md +++ b/Readme.md @@ -140,6 +140,8 @@ When establishing a connection, you can set the following options: * `database`: Name of the database to use for this connection (Optional). * `charset`: The charset for the connection. (Default: `'UTF8_GENERAL_CI'`. Value needs to be all in upper case letters!) * `timezone`: The timezone used to store local dates. (Default: `'local'`) +* `connectTimeout`: The milliseconds before a timeout occurs during the initial connection + to the MySQL server. (Default: no timeout) * `stringifyObjects`: Stringify objects instead of converting to values. See issue [#501](https://github.com/felixge/node-mysql/issues/501). (Default: `'false'`) * `insecureAuth`: Allow connecting to MySQL instances that ask for the old diff --git a/lib/Connection.js b/lib/Connection.js index b2b6a5f2c..8f75b6327 100644 --- a/lib/Connection.js +++ b/lib/Connection.js @@ -76,6 +76,15 @@ Connection.prototype.connect = function(cb) { this._protocol.on('unhandledError', this._handleProtocolError.bind(this)); this._protocol.on('drain', this._handleProtocolDrain.bind(this)); this._protocol.on('end', this._handleProtocolEnd.bind(this)); + + if (this.config.connectTimeout) { + var handleConnectTimeout = this._handleConnectTimeout.bind(this); + + this._socket.setTimeout(this.config.connectTimeout, handleConnectTimeout); + this._socket.once('connect', function() { + this.setTimeout(0, handleConnectTimeout); + }); + } } this._protocol.handshake(cb); @@ -191,6 +200,20 @@ Connection.prototype.format = function(sql, values) { return SqlString.format(sql, values, this.config.stringifyObjects, this.config.timezone); }; +Connection.prototype._handleConnectTimeout = function() { + if (this._socket) { + this._socket.setTimeout(0); + this._socket.destroy(); + } + + var err = new Error('connect ETIMEDOUT'); + err.errorno = 'ETIMEDOUT'; + err.code = 'ETIMEDOUT'; + err.syscall = 'connect'; + + this._handleNetworkError(err); +}; + Connection.prototype._handleNetworkError = function(err) { this._protocol.handleNetworkError(err); }; diff --git a/lib/ConnectionConfig.js b/lib/ConnectionConfig.js index d6e0a5168..d0fb4f6e6 100644 --- a/lib/ConnectionConfig.js +++ b/lib/ConnectionConfig.js @@ -15,6 +15,7 @@ function ConnectionConfig(options) { this.user = options.user || undefined; this.password = options.password || undefined; this.database = options.database; + this.connectTimeout = options.connectTimeout || undefined; this.insecureAuth = options.insecureAuth || false; this.supportBigNumbers = options.supportBigNumbers || false; this.bigNumberStrings = options.bigNumberStrings || false; diff --git a/test/integration/connection/test-connect-timeout-only.js b/test/integration/connection/test-connect-timeout-only.js new file mode 100644 index 000000000..f5b90cab8 --- /dev/null +++ b/test/integration/connection/test-connect-timeout-only.js @@ -0,0 +1,22 @@ +var common = require('../../common'); +var connection = common.createConnection({connectTimeout: 1000}); +var assert = require('assert'); + +connection.connect(); + +var connectErr; +var rows = undefined; +var fields = undefined; +connection.query('SELECT SLEEP(3)', function(err, _rows, _fields) { + connectErr = err; + rows = _rows; + fields = _fields; +}); + +connection.end(); + +process.on('exit', function() { + assert.ifError(connectErr); + assert.deepEqual(rows, [{'SLEEP(3)': 0}]); + assert.equal(fields[0].name, 'SLEEP(3)'); +}); diff --git a/test/integration/connection/test-connect-timeout.js b/test/integration/connection/test-connect-timeout.js new file mode 100644 index 000000000..bfe9d063e --- /dev/null +++ b/test/integration/connection/test-connect-timeout.js @@ -0,0 +1,20 @@ +var common = require('../../common'); +var connection = common.createConnection({host: '1.1.1.1', port: common.fakeServerPort, connectTimeout: 500}); +var assert = require('assert'); + +var testTimeout = setTimeout(function() { + connection.destroy(); +}, 5000); + +var connectErr; +connection.connect(function(err) { + connectErr = err; + clearTimeout(testTimeout); +}); + +process.on('exit', function() { + assert.ok(connectErr); + assert.equal(connectErr.code, 'ETIMEDOUT'); + assert.equal(connectErr.syscall, 'connect'); + assert.equal(connectErr.fatal, true); +});