Improve client reconnect behavior

A client configured to reconnect should do so on socket close unless
unbind was called explicitly.  This covers cases where the connection
undergoes clean but unexpected termination.
This commit is contained in:
Patrick Mooney 2014-06-27 16:15:04 -05:00
parent 3edf9de578
commit c7cd5da208
3 changed files with 81 additions and 43 deletions

View File

@ -678,6 +678,10 @@ Client.prototype.unbind = function unbind(callback) {
if (typeof (callback) !== 'function') if (typeof (callback) !== 'function')
throw new TypeError('callback must be a function'); throw new TypeError('callback must be a function');
// When the socket closes, it is useful to know whether it was due to a
// user-initiated unbind or something else.
this.unbound = true;
if (!this.socket) if (!this.socket)
return callback(); return callback();
@ -1023,9 +1027,13 @@ Client.prototype._onClose = function _onClose(had_err) {
}); });
delete socket.ldap.parser; delete socket.ldap.parser;
delete socket.ldap; delete socket.ldap;
if (had_err && this.reconnect) {
// Automatically fire reconnect logic if the socket was closed for any reason
// other than a user-initiated unbind.
if (this.reconnect && !this.unbound) {
this._connect(); this._connect();
} }
this.unbound = false;
return false; return false;
}; };

View File

@ -15,6 +15,14 @@ var SOCKET = '/tmp/.' + uuid();
var SUFFIX = 'dc=test'; var SUFFIX = 'dc=test';
var LOG = new Logger({
name: 'ldapjs_unit_test',
stream: process.stderr,
level: (process.env.LOG_LEVEL || 'info'),
serializers: Logger.stdSerializers,
src: true
});
var ldap; var ldap;
var Attribute; var Attribute;
var Change; var Change;
@ -148,13 +156,7 @@ test('setup', function (t) {
client = ldap.createClient({ client = ldap.createClient({
connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT || 0, 10), connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT || 0, 10),
socketPath: SOCKET, socketPath: SOCKET,
log: new Logger({ log: LOG
name: 'ldapjs_unit_test',
stream: process.stderr,
level: (process.env.LOG_LEVEL || 'info'),
serializers: Logger.stdSerializers,
src: true
})
}); });
t.ok(client); t.ok(client);
t.end(); t.end();
@ -622,13 +624,7 @@ test('setup action', function (t) {
var setupClient = ldap.createClient({ var setupClient = ldap.createClient({
connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT || 0, 10), connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT || 0, 10),
socketPath: SOCKET, socketPath: SOCKET,
log: new Logger({ log: LOG
name: 'ldapjs_unit_test',
stream: process.stderr,
level: (process.env.LOG_LEVEL || 'info'),
serializers: Logger.stdSerializers,
src: true
})
}); });
setupClient.on('setup', function (clt, cb) { setupClient.on('setup', function (clt, cb) {
clt.bind(BIND_DN, BIND_PW, function (err, res) { clt.bind(BIND_DN, BIND_PW, function (err, res) {
@ -652,13 +648,7 @@ test('setup reconnect', function (t) {
connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT || 0, 10), connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT || 0, 10),
socketPath: SOCKET, socketPath: SOCKET,
reconnect: true, reconnect: true,
log: new Logger({ log: LOG
name: 'ldapjs_unit_test',
stream: process.stderr,
level: (process.env.LOG_LEVEL || 'info'),
serializers: Logger.stdSerializers,
src: true
})
}); });
rClient.on('setup', function (clt, cb) { rClient.on('setup', function (clt, cb) {
clt.bind(BIND_DN, BIND_PW, function (err, res) { clt.bind(BIND_DN, BIND_PW, function (err, res) {
@ -715,13 +705,7 @@ test('setup abort', function (t) {
connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT || 0, 10), connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT || 0, 10),
socketPath: SOCKET, socketPath: SOCKET,
reconnect: true, reconnect: true,
log: new Logger({ log: LOG
name: 'ldapjs_unit_test',
stream: process.stderr,
level: (process.env.LOG_LEVEL || 'info'),
serializers: Logger.stdSerializers,
src: true
})
}); });
var message = 'It\'s a trap!'; var message = 'It\'s a trap!';
setupClient.on('setup', function (clt, cb) { setupClient.on('setup', function (clt, cb) {
@ -743,13 +727,7 @@ test('abort reconnect', function (t) {
connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT || 0, 10), connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT || 0, 10),
socketPath: '/dev/null', socketPath: '/dev/null',
reconnect: true, reconnect: true,
log: new Logger({ log: LOG
name: 'ldapjs_unit_test',
stream: process.stderr,
level: (process.env.LOG_LEVEL || 'info'),
serializers: Logger.stdSerializers,
src: true
})
}); });
var retryCount = 0; var retryCount = 0;
abortClient.on('connectError', function () { abortClient.on('connectError', function () {
@ -777,13 +755,7 @@ test('reconnect max retries', function (t) {
initialDelay: 10, initialDelay: 10,
maxDelay: 100 maxDelay: 100
}, },
log: new Logger({ log: LOG
name: 'ldapjs_unit_test',
stream: process.stderr,
level: (process.env.LOG_LEVEL || 'info'),
serializers: Logger.stdSerializers,
src: true
})
}); });
var count = 0; var count = 0;
rClient.on('connectError', function () { rClient.on('connectError', function () {
@ -797,6 +769,63 @@ test('reconnect max retries', function (t) {
}); });
test('reconnect on server close', function (t) {
var clt = ldap.createClient({
socketPath: SOCKET,
reconnect: true,
log: LOG
});
clt.on('setup', function (sclt, cb) {
sclt.bind(BIND_DN, BIND_PW, function (err, res) {
t.ifError(err);
cb(err);
});
});
clt.once('connect', function () {
t.ok(clt.socket);
clt.once('connect', function () {
t.ok(true, 'successful reconnect');
clt.destroy();
t.end();
});
// Simulate server-side close
clt.socket.destroy();
});
});
test('no auto-reconnect on unbind', function (t) {
var clt = ldap.createClient({
socketPath: SOCKET,
reconnect: true,
log: LOG
});
clt.on('setup', function (sclt, cb) {
sclt.bind(BIND_DN, BIND_PW, function (err, res) {
t.ifError(err);
cb(err);
});
});
clt.once('connect', function () {
clt.once('connect', function () {
t.ifError(new Error('client should not reconnect'));
});
clt.once('close', function () {
t.ok(true, 'initial close');
setImmediate(function () {
t.ok(!clt.connected, 'should not be connected');
t.ok(!clt.connecting, 'should not be connecting');
clt.destroy();
t.end();
});
});
clt.unbind();
});
});
test('abandon (GH-27)', function (t) { test('abandon (GH-27)', function (t) {
client.abandon(401876543, function (err) { client.abandon(401876543, function (err) {
t.ifError(err); t.ifError(err);

View File

@ -119,6 +119,7 @@
+define process +define process
+define require +define require
+define setInterval +define setInterval
+define setImmediate
+define setTimeout +define setTimeout
+define Buffer +define Buffer
+define JSON +define JSON